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

        bwb_fnc.c       Function Interpretation Routines
                        for Bywater BASIC Interpreter

                        Copyright (c) 1992, Ted A. Campbell

                        Bywater Software
                        P. O. Box 4023
                        Duke Station
                        Durham, NC  27706

                        email: tcamp@acpub.duke.edu

        Copyright and Permissions Information:

        All U.S. and international copyrights are claimed by the
        author. The author grants permission to use this code
        and software based on it under the following conditions:
        (a) in general, the code and software based upon it may be
        used by individuals and by non-profit organizations; (b) it
        may also be utilized by governmental agencies in any country,
        with the exception of military agencies; (c) the code and/or
        software based upon it may not be sold for a profit without
        an explicit and specific permission from the author, except
        that a minimal fee may be charged for media on which it is
        copied, and for copying and handling; (d) the code must be
        distributed in the form in which it has been released by the
        author; and (e) the code and software based upon it may not
        be used for illegal activities.

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

#define FSTACKSIZE      32

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

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

#ifndef RAND_MAX			/* added in v1.11 */
#define RAND_MAX	32767
#endif

static time_t t;
static struct tm *lt;

struct bwb_function fnc_start, fnc_end;

int ufsc = -1;                   /* user function stack counter */

struct bwb_function bwb_prefuncs[ FUNCTIONS ] =
   {
   { "ABS",     DOUBLE,         1,  (struct user_fnc *) NULL,  fnc_abs,        (struct bwb_function *) NULL    },
   { "DATE$",   STRING,         0,  (struct user_fnc *) NULL,  fnc_date,       (struct bwb_function *) NULL    },
   { "TIME$",   STRING,         0,  (struct user_fnc *) NULL,  fnc_time,       (struct bwb_function *) NULL    },
   { "ATN",     DOUBLE,         1,  (struct user_fnc *) NULL,  fnc_atn,        (struct bwb_function *) NULL    },
   { "COS",     DOUBLE,         1,  (struct user_fnc *) NULL,  fnc_cos,        (struct bwb_function *) NULL    },
   { "LOG",     DOUBLE,         1,  (struct user_fnc *) NULL,  fnc_log,        (struct bwb_function *) NULL    },
   { "SIN",     DOUBLE,         1,  (struct user_fnc *) NULL,  fnc_sin,        (struct bwb_function *) NULL    },
   { "SQR",     DOUBLE,         1,  (struct user_fnc *) NULL,  fnc_sqr,        (struct bwb_function *) NULL    },
   { "TAN",     DOUBLE,         1,  (struct user_fnc *) NULL,  fnc_tan,        (struct bwb_function *) NULL    },
   { "SGN",     DOUBLE,         1,  (struct user_fnc *) NULL,  fnc_sgn,        (struct bwb_function *) NULL    },
   { "INT",     DOUBLE,         1,  (struct user_fnc *) NULL,  fnc_int,        (struct bwb_function *) NULL    },
   { "RND",     DOUBLE,         0,  (struct user_fnc *) NULL,  fnc_rnd,        (struct bwb_function *) NULL    },
   { "CHR$",    DOUBLE,         0,  (struct user_fnc *) NULL,  fnc_chr,        (struct bwb_function *) NULL    },
   { "TAB",     STRING,		1,  (struct user_fnc *) NULL,  fnc_tab,        (struct bwb_function *) NULL    },
   { "SPC",     STRING,		1,  (struct user_fnc *) NULL,  fnc_spc,        (struct bwb_function *) NULL    },
   { "SPACE$",  STRING,		1,  (struct user_fnc *) NULL,  fnc_space,      (struct bwb_function *) NULL    },
   { "STRING$", STRING,		1,  (struct user_fnc *) NULL,  fnc_string,     (struct bwb_function *) NULL    },
   { "MID$",    STRING,		3,  (struct user_fnc *) NULL,  fnc_mid,        (struct bwb_function *) NULL    },
   { "LEFT$",   STRING,		2,  (struct user_fnc *) NULL,  fnc_left,       (struct bwb_function *) NULL    },
   { "RIGHT$",  STRING,		2,  (struct user_fnc *) NULL,  fnc_right,      (struct bwb_function *) NULL    },
   { "TIMER",   SINGLE,         0,  (struct user_fnc *) NULL,  fnc_timer,      (struct bwb_function *) NULL    },
   { "VAL",     INTEGER,        1,  (struct user_fnc *) NULL,  fnc_val,        (struct bwb_function *) NULL    },
   { "POS",     INTEGER,        0,  (struct user_fnc *) NULL,  fnc_pos,        (struct bwb_function *) NULL    },
   { "ERR",     INTEGER,        0,  (struct user_fnc *) NULL,  fnc_err,        (struct bwb_function *) NULL    },
   { "ERL",     INTEGER,        0,  (struct user_fnc *) NULL,  fnc_erl,        (struct bwb_function *) NULL    },
   { "LEN",     INTEGER,        1,  (struct user_fnc *) NULL,  fnc_len,        (struct bwb_function *) NULL    },
   { "LOC",     INTEGER,        1,  (struct user_fnc *) NULL,  fnc_loc,        (struct bwb_function *) NULL    },
   { "EOF",     DOUBLE,         1,  (struct user_fnc *) NULL,  fnc_eof,        (struct bwb_function *) NULL    },
   { "CSNG",    SINGLE,         1,  (struct user_fnc *) NULL,  fnc_csng,       (struct bwb_function *) NULL    },
   { "EXP",     SINGLE,         1,  (struct user_fnc *) NULL,  fnc_exp,        (struct bwb_function *) NULL    },
   { "INSTR",   INTEGER,        1,  (struct user_fnc *) NULL,  fnc_instr,      (struct bwb_function *) NULL    },
   { "STR$",    STRING,         1,  (struct user_fnc *) NULL,  fnc_str,        (struct bwb_function *) NULL    },
   { "HEX$",    STRING,         1,  (struct user_fnc *) NULL,  fnc_hex,        (struct bwb_function *) NULL    },
   { "OCT$",    STRING,         1,  (struct user_fnc *) NULL,  fnc_oct,        (struct bwb_function *) NULL    },
   { "CINT",    SINGLE,         1,  (struct user_fnc *) NULL,  fnc_cint,       (struct bwb_function *) NULL    },
   { "ASC",     SINGLE,         1,  (struct user_fnc *) NULL,  fnc_asc,        (struct bwb_function *) NULL    },
   { "ENVIRON$",STRING,         1,  (struct user_fnc *) NULL,  fnc_environ,    (struct bwb_function *) NULL    },
#if UNIX_CMDS
   { "LOF",     DOUBLE,         1,  (struct user_fnc *) NULL,  fnc_lof,        (struct bwb_function *) NULL    },
#endif
#if INTENSIVE_DEBUG
   { "TEST",    DOUBLE,         2,  (struct user_fnc *) NULL,  fnc_test,       (struct bwb_function *) NULL    },
#endif
   { "MKD$",    STRING,         1,  (struct user_fnc *) NULL,  fnc_mkd,        (struct bwb_function *) NULL    },
   { "MKI$",    STRING,         1,  (struct user_fnc *) NULL,  fnc_mki,        (struct bwb_function *) NULL    },
   { "MKS$",    STRING,         1,  (struct user_fnc *) NULL,  fnc_mks,        (struct bwb_function *) NULL    },
   { "CVD",     DOUBLE,         1,  (struct user_fnc *) NULL,  fnc_cvd,        (struct bwb_function *) NULL    },
   { "CVS",     SINGLE,         1,  (struct user_fnc *) NULL,  fnc_cvs,        (struct bwb_function *) NULL    },
   { "CVI",     INTEGER,        1,  (struct user_fnc *) NULL,  fnc_cvi,        (struct bwb_function *) NULL    }
   };

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

        FUNCTION:       fnc_init()

        DESCRIPTION:    This command initializes the function
                        linked list, placing all predefined functions
                        in the list.

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

int
fnc_init()
   {
   register int n;
   struct bwb_function *f;

   strcpy( fnc_start.name, "FNC_START" );
   fnc_start.type = 'X';
   fnc_start.vector = fnc_null;
   strcpy( fnc_end.name, "FNC_END" );
   fnc_end.type = 'x';
   fnc_end.vector = fnc_null;
   fnc_end.next = &fnc_end;

   f = &fnc_start;

   /* now go through each of the preestablished functions and set up
      links between them; from this point the program address the functions
      only as a linked list (not as an array) */

   for ( n = 0; n < FUNCTIONS; ++n )
      {
      f->next = &( bwb_prefuncs[ n ] );
      f = f->next;
      }

   /* link the last pointer to the end; this completes the list */

   f->next = &fnc_end;

   return TRUE;
   }

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

        FUNCTION:       fnc_find()

        DESCRIPTION:    This C function attempts to locate
                        a BASIC function with the specified name.
                        If successful, it returns a pointer to
                        the C structure for the BASIC function,
                        if not successful, it returns NULL.

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

struct bwb_function *
fnc_find( char *buffer )
   {
   struct bwb_function * f;
   register int n;
   static char *tbuf;
   static int init = FALSE;

   /* get memory for temporary buffer if necessary */

   if ( init == FALSE )
      {
      init = TRUE;
      if ( ( tbuf = calloc( MAXSTRINGSIZE + 1, sizeof( char ) )) == NULL )
         {
         bwb_error( err_getmem );
         }
      }

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in fnc_find(): called for <%s> ", buffer );
   bwb_debug( bwb_ebuf );
#endif

   for ( n = 0; buffer[ n ] != 0; ++n )
      {
      if ( islower( buffer[ n ] ) )
         {
         tbuf[ n ] = toupper( buffer[ n ] );
         }
      else
         {
         tbuf[ n ] = buffer[ n ];
         }
      }
   tbuf[ n ] = 0;

   for ( f = fnc_start.next; f != &fnc_end; f = f->next )
      {
      if ( strcmp( f->name, tbuf ) == 0 )
         {
#if INTENSIVE_DEBUG
         sprintf( bwb_ebuf, "in fnc_find(): found <%s> ", f->name );
         bwb_debug( bwb_ebuf );
#endif
         return f;
         }
      }

   /* search has failed: return NULL */

   return NULL;

   }

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

        FUNCTION:       bwb_deffn()

        DESCRIPTION:    This C function implements the BASIC
                        DEF FNxx statement.

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

struct bwb_line *
bwb_deffn( struct bwb_line *l )
   {
   register int n;
   int loop, arguments, p;
   struct bwb_function *f, *fncpos;
   static char *tbuf;
   static int init = FALSE;

   /* get memory for temporary buffer if necessary */

   if ( init == FALSE )
      {
      init = TRUE;
      if ( ( tbuf = calloc( MAXSTRINGSIZE + 1, sizeof( char ) )) == NULL )
         {
         bwb_error( err_getmem );
         }
      }

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

   /* test for appropriate function name */

   exp_getvfname( &( l->buffer[ l->startpos ] ), tbuf );     /* name in tbuf */

   for ( n = 0; tbuf[ n ] != '\0'; ++n )
      {
      if ( islower( tbuf[ n ] ) != FALSE )
         {
         tbuf[ n ] = toupper( tbuf[ n ] );
         }
      }

   if ( strncmp( tbuf, "FN", (size_t) 2 ) != 0 )
      {
#if PROG_ERRORS
      sprintf( bwb_ebuf, "at line %d: User-defined function name must begin with FN.",
         l->number );
      bwb_error( bwb_ebuf );
#else
      bwb_error( err_syntax );
#endif
      l->next->position = 0;
      return l->next;
      }

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

   /* Allocate memory for a new function structure */

   if ( ( f = (struct bwb_function *) calloc( (size_t) 1, sizeof( struct bwb_function ) )) == NULL )
      {
#if PROG_ERRORS
      sprintf( bwb_ebuf, "Failed to find memory for function structure." );
      bwb_error( bwb_ebuf );
#else
      bwb_error( err_getmem );
#endif
      l->next->position = 0;
      return l->next;
      }

   /* Allocate memory for a user function structure */

   if ( ( f->ufnc = (struct user_fnc *) calloc( (size_t) 1, sizeof( struct user_fnc ) )) == NULL )
      {
#if PROG_ERRORS
      sprintf( bwb_ebuf, "Failed to find memory for function structure." );
      bwb_error( bwb_ebuf );
#else
      bwb_error( err_getmem );
#endif
      l->next->position = 0;
      return l->next;
      }

   /* Set some values for the new function */

   strncpy( f->name, tbuf, (size_t) MAXVARNAMESIZE );

   switch( f->name[ strlen( f->name ) - 1 ] )
      {
      case STRING:
      case DOUBLE:
      case INTEGER:
         f->type = f->name[ strlen( f->name ) - 1 ];
         break;
      default:
         f->type = SINGLE;
         break;
      }

   f->vector = NULL;
   f->arguments = 0;

   /* determine if there are arguments */

   loop = TRUE;
   arguments = FALSE;
   l->position += strlen( f->name );
   while( loop == TRUE )
      {

      switch( l->buffer[ l->position ] )
         {
         case ' ':                      /* whitespace */
         case '\t':
            ++l->position;
            break;
         case '(':                      /* begin parenthesis = arguments */
            ++l->position;
            loop = FALSE;
            arguments = TRUE;
            break;
         case '\n':                     /* unexpected end of line */
         case '\r':
         case '\0':
#if PROG_ERRORS
            sprintf( bwb_ebuf, "at line %d: Unexpected end of line", l->number );
            bwb_error( bwb_ebuf );
#else
            bwb_error( err_syntax );
#endif
            l->next->position = 0;
            return l->next;
         default:                       /* any other character = no arguments */
            loop = FALSE;
            break;
         }

      }

   /* identify arguments */

   if ( arguments == TRUE )
      {

      loop = TRUE;
      f->arguments = 0;                              /* use as counter */
      p = 0;
      f->ufnc->user_vns[ f->arguments ][ 0 ] = '\0';
      while ( loop == TRUE )
         {
         switch( l->buffer[ l->position ] )
            {
            case ' ':                           /* whitespace */
            case '\t':
               ++l->position;
               break;
            case '\0':                          /* unexpected end of line */
            case '\n':
            case '\r':
#if PROG_ERRORS
               sprintf( bwb_ebuf, "at line %d: Unexpected end of line.",
                  l->number );
               bwb_error( bwb_ebuf );
#else
               bwb_error( err_syntax );
#endif
               l->next->position = 0;
               return l->next;
            case ')':                           /* end of argument list */
               ++f->arguments;                  /* returns total number of arguments */
               ++l->position;                   /* advance beyond parenthesis */
               loop = FALSE;
               break;

            case ',':                           /* end of one argument */

               ++f->arguments;
               ++l->position;
               p = 0;
               f->ufnc->user_vns[ f->arguments ][ 0 ] = '\0';
               break;
            default:

               f->ufnc->user_vns[ f->arguments ][ p ] = l->buffer[ l->position ];
               ++l->position;
               ++p;
               f->ufnc->user_vns[ f->arguments ][ p ] = '\0';
               break;
            }
         }

      }

   /* else no arguments were found */

   else
      {
      f->arguments = 0;
      }

#if INTENSIVE_DEBUG
   for ( n = 0; n < f->arguments; ++n )
      {
      sprintf( bwb_ebuf, "in bwb_deffn(): argument <%d> name <%s>.",
         n, f->ufnc->user_vns[ n ] );
      bwb_debug( bwb_ebuf );
      }
#endif

   /* find the string to be interpreted */

   loop = TRUE;
   arguments = FALSE;
   while( loop == TRUE )
      {
      switch( l->buffer[ l->position ] )
         {
         case '\0':                     /* unexpected end of line */
         case '\n':
         case '\r':
#if PROG_ERRORS
            sprintf( bwb_ebuf, "at line %d: Unexpected end of line.",
               l->number );
            bwb_error( bwb_ebuf );
#else
            bwb_error( err_syntax );
#endif
            l->next->position = 0;
            return l->next;
         case ' ':                      /* whitespace */
         case '\t':
            ++l->position;
            break;

         case '=':
            ++l->position;
            arguments = TRUE;
            break;
         default:
            loop = FALSE;
            break;
         }
      }

   /* if the equals sign was not detected, return error */

   if ( arguments == FALSE )
      {
#if PROG_ERRORS
      sprintf( bwb_ebuf, "at line %d: Assignment operator (=) not found.",
         l->number );
      bwb_error( bwb_ebuf );
#else
      bwb_error( err_syntax );
#endif
      l->next->position = 0;
      return l->next;
      }

   /* write the string to be interpreted to the user function structure */

   strncpy( f->ufnc->int_line, &( l->buffer[ l->position ] ),
      (size_t) MAXSTRINGSIZE );

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in bwb_deffn(): line <%s>", f->ufnc->int_line );
   bwb_debug( bwb_ebuf );
#endif

   /* Place the new function in the function linked list */

   for ( fncpos = &fnc_start; fncpos->next != &fnc_end; fncpos = fncpos->next )
      {
      ;
      }
   fncpos->next = f;
   f->next = &fnc_end;

   /* return */

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

   }

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

        FUNCTION:       fnc_intufnc()

        DESCRIPTION:    This C function interprets a user-defined
                        BASIC function.

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

struct bwb_variable *
fnc_intufnc( int argc, struct bwb_variable *argv, struct bwb_function *f )
   {
   register int n;
   int l_position, f_position;
   int written;
   bstring *b;
   struct exp_ese *e;
   static struct bwb_variable nvar;

#if INTENSIVE_DEBUG
   sprintf( nvar.name, "intufnc variable" );
#endif

   /* increment the user function stack counter */

   if ( ufsc >= UFNCSTACKSIZE )
      {
#if PROG_ERRORS
      sprintf( bwb_ebuf, "exceeded user-defined function stack, level <%d>",
         ufsc );
      bwb_error( bwb_ebuf );
#else
      bwb_error( err_overflow );
#endif
      }

   ++ufsc;

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in fnc_intufnc(): interpreting user function <%s>",
      f->name );
   bwb_debug( bwb_ebuf );
#endif

   /* print arguments to strings */

   for ( n = 1; n <= argc; ++n )
      {
      switch( argv[ n - 1 ].type )
         {
         case DOUBLE:
            sprintf( ufs[ ufsc ].args[ n - 1 ], "(%f)",
               var_getdval( &( argv[ n - 1 ] ) ));
            break;
         case SINGLE:
            sprintf( ufs[ ufsc ].args[ n - 1 ], "(%f)",
               var_getfval( &( argv[ n - 1 ] ) ));
            break;
         case INTEGER:
            sprintf( ufs[ ufsc ].args[ n - 1 ], "(%d)",
               var_getival( &( argv[ n - 1 ] ) ));
            break;
         case STRING:
            b = var_getsval( &( argv[ n - 1 ] ) );
            str_btoc( bwb_ebuf, b );
            sprintf( ufs[ ufsc ].args[ n - 1 ], "\"%s\"",
               bwb_ebuf );
            break;
         default:
#if PROG_ERRORS
            sprintf( bwb_ebuf, "Unidentified variable type in argument to user function." );
            bwb_error( bwb_ebuf );
#else
            bwb_error( err_mismatch );
#endif
            return &nvar;
            }
      }

#if INTENSIVE_DEBUG
   for ( n = 1; n <= argc; ++n )
      {
      sprintf( bwb_ebuf, "in fnc_intufnc(): arg string %d: <%s>.",
         n - 1, ufs[ ufsc ].args[ n - 1 ] );
      bwb_debug ( bwb_ebuf );
      }
#endif

   /* copy the interpreted line to the buffer, substituting variable ufs[ ufsc ].args */

   ufs[ ufsc ].l_buffer[ 0 ] = '\0';
   l_position = 0;
   for ( f_position = 0; f->ufnc->int_line[ f_position ] != '\0'; ++f_position )
      {
      written = FALSE;
      for ( n = 0; n < argc; ++n )
         {
         if ( strncmp( &( f->ufnc->int_line[ f_position ] ), f->ufnc->user_vns[ n ],
            (size_t) strlen( f->ufnc->user_vns[ n ] ) ) == 0 )
            {
            strcat( ufs[ ufsc ].l_buffer, ufs[ ufsc ].args[ n ] );
            written = TRUE;
            f_position += strlen( f->ufnc->user_vns[ n ] + 1 );
            l_position += strlen( ufs[ ufsc ].args[ n ] );
            }

         }
      if ( written == FALSE )
         {
         ufs[ ufsc ].l_buffer[ l_position ] = f->ufnc->int_line[ f_position ];
         ++l_position;
         ufs[ ufsc ].l_buffer[ l_position ] = '\0';
         }
      }

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in fnc_intufnc(): reconstructed line: <%s>",
      ufs[ ufsc ].l_buffer );
   bwb_debug( bwb_ebuf );
#endif

   /* parse */

   ufs[ ufsc ].position = 0;
   e = bwb_exp( ufs[ ufsc ].l_buffer, FALSE,
      &( ufs[ ufsc ].position ) );

   var_make( &nvar, e->type );

   switch( e->type )
      {
      case DOUBLE:
         * var_finddval( &nvar, nvar.array_pos ) = exp_getdval( e );
         break;
      case INTEGER:
         * var_findival( &nvar, nvar.array_pos ) = exp_getival( e );
         break;
      case STRING:
         str_btob( var_findsval( &nvar, nvar.array_pos ), 
            exp_getsval( e ) );
         break;
      default:
         * var_findfval( &nvar, nvar.array_pos ) = exp_getfval( e );
         break;
      }

   /* decrement the user function stack counter */

   --ufsc;

   return &nvar;

   }

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

        FUNCTION:       fnc_null()

        DESCRIPTION:    This is a null function that can be used
                        to fill in a required function-structure
                        pointer when needed.

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

struct bwb_variable *
fnc_null( int argc, struct bwb_variable *argv )
   {
   static struct bwb_variable nvar;
   static int init = FALSE;

   /* initialize the variable if necessary */

   if ( init == FALSE )
      {
      init = TRUE;
      var_make( &nvar, INTEGER );
      }

   return &nvar;
   }

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


        FUNCTION:       fnc_date()

        DESCRIPTION:    This C function implements the BASIC
                        predefined DATE$ function, returning
                        a string containing the year, month,
                        and day of the month.

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

struct bwb_variable *
fnc_date( int argc, struct bwb_variable *argv )
   {
   static struct bwb_variable nvar;
   static int init = FALSE;
   static char *tbuf;

   /* initialize the variable if necessary */

   if ( init == FALSE )
      {
      init = TRUE;
      var_make( &nvar, STRING );
      if ( ( tbuf = calloc( MAXSTRINGSIZE + 1, sizeof( char ) )) == NULL )
         {
         bwb_error( err_getmem );
         }
      }

   time( &t );
   lt = localtime( &t );

   sprintf( tbuf, "%02d-%02d-%04d", lt->tm_mon + 1, lt->tm_mday,
      1900 + lt->tm_year );
   str_ctob( var_findsval( &nvar, nvar.array_pos ), tbuf );

   return &nvar;
   }

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

        FUNCTION:       fnc_time()

        DESCRIPTION:    This C function implements the BASIC
                        predefined TIME$ function, returning a
                        string containing the hour, minute, and
                        second count.

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

struct bwb_variable *
fnc_time( int argc, struct bwb_variable *argv )
   {
   static struct bwb_variable nvar;
   static char *tbuf;
   static int init = FALSE;

   /* initialize the variable if necessary */

   if ( init == FALSE )
      {
      init = TRUE;
      var_make( &nvar, STRING );
      if ( ( tbuf = calloc( MAXSTRINGSIZE + 1, sizeof( char ) )) == NULL )
         {
         bwb_error( err_getmem );
         }
      }

   time( &t );
   lt = localtime( &t );

   sprintf( tbuf, "%02d:%02d:%02d", lt->tm_hour, lt->tm_min,
      lt->tm_sec );
   str_ctob( var_findsval( &nvar, nvar.array_pos ), tbuf );

   return &nvar;
   }

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

        FUNCTION:       fnc_test()

        DESCRIPTION:    This is a test function, developed in
                        order to test argument passing to
                        BASIC functions.

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

#if INTENSIVE_DEBUG
struct bwb_variable *
fnc_test( int argc, struct bwb_variable *argv )
   {
   register int c;
   static struct bwb_variable rvar;
   static char *tbuf;
   static int init = FALSE;

   /* initialize the variable if necessary */

   if ( init == FALSE )
      {
      init = TRUE;
      var_make( &rvar, SINGLE );
      if ( ( tbuf = calloc( MAXSTRINGSIZE + 1, sizeof( char ) )) == NULL )
         {
         bwb_error( err_getmem );
         }
      }

   fprintf( stdout, "TEST function: received %d arguments: \n", argc );

   for ( c = 0; c < argc; ++c )
      {
      str_btoc( tbuf, var_getsval( &argv[ c ] ) );
      fprintf( stdout, "                  arg %d (%c): <%s> \n", c,
         argv[ c ].type, tbuf );
      }

   return &rvar;

   }
#endif

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

        FUNCTION:       fnc_rnd()

        DESCRIPTION:    This C function implements the BASIC
                        predefined RND function, returning a
                        pseudo-random number in the range
                        0 to 1.  It is affected by the RANDOMIZE
                        command statement.

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

struct bwb_variable *
fnc_rnd( int argc, struct bwb_variable *argv  )
   {
   static struct bwb_variable nvar;
   static int init = FALSE;

   /* initialize the variable if necessary */

   if ( init == FALSE )
      {
      init = TRUE;
      var_make( &nvar, SINGLE );
      }

   * var_findfval( &nvar, nvar.array_pos ) = (float) rand() / RAND_MAX;

   return &nvar;
   }

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

        FUNCTION:       fnc_chr()

        DESCRIPTION:    This C function implements the BASIC
                        predefined CHR$ function, returning a
                        string containing the single character
                        whose ASCII value is the argument to
                        this function.

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

struct bwb_variable *
fnc_chr( int argc, struct bwb_variable *argv  )
   {
   static struct bwb_variable nvar;
   char tbuf[ MAXSTRINGSIZE + 1 ];
   static int init = FALSE;
#if TEST_BSTRING
   bstring *b;
#endif

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in fnc_chr(): entered function, argc <%d>",
      argc );
   bwb_debug( bwb_ebuf );
#endif

   /* initialize the variable if necessary */

   if ( init == FALSE )
      {
      init = TRUE;
      var_make( &nvar, STRING );
#if INTENSIVE_DEBUG
      sprintf( bwb_ebuf, "in fnc_chr(): entered function, initialized nvar" );
      bwb_debug( bwb_ebuf );
#endif
      }

   /* check arguments */

#if PROG_ERRORS
   if ( argc < 1 )
      {
      sprintf( bwb_ebuf, "Not enough arguments to function CHR$()" );
      bwb_error( bwb_ebuf );
      return NULL;
      }
   else if ( argc > 1 )
      {
      sprintf( bwb_ebuf, "Too many parameters (%d) to function CHR$().",
         argc );
      bwb_error( bwb_ebuf );
      return NULL;
      }
#else
   if ( fnc_checkargs( argc, argv, 1, 1 ) == FALSE )
      {
      return NULL;
      }
#endif

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

   tbuf[ 0 ] = (char) var_getival( &( argv[ 0 ] ) );
   tbuf[ 1 ] = '\0';
   str_ctob( var_findsval( &nvar, nvar.array_pos ), tbuf );

#if TEST_BSTRING
   b = var_findsval( &nvar, nvar.array_pos );
   sprintf( bwb_ebuf, "in fnc_chr(): bstring name is <%s>", b->name );
   bwb_debug( bwb_ebuf );
#endif
#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in fnc_chr(): tbuf[ 0 ] is <%c>", tbuf[ 0 ] );
   bwb_debug( bwb_ebuf );
#endif

   return &nvar;
   }

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

        FUNCTION:       fnc_mid()

        DESCRIPTION:    This C function implements the BASIC
                        predefined MID$ function

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

struct bwb_variable *
fnc_mid( int argc, struct bwb_variable *argv  )
   {
   static struct bwb_variable nvar;
   register int c;
   char target_string[ MAXSTRINGSIZE + 1 ];
   int target_counter, num_spaces;
   char tbuf[ MAXSTRINGSIZE + 1 ];
   static int init = FALSE;

   /* initialize the variable if necessary */

   if ( init == FALSE )
      {
      init = TRUE;
      var_make( &nvar, STRING );
      }

   /* check arguments */

#if PROG_ERRORS
   if ( argc < 2 )
      {
      sprintf( bwb_ebuf, "Not enough arguments to function MID$()" );
      bwb_error( bwb_ebuf );
      return &nvar;
      }

   if ( argc > 3 )
      {
      sprintf( bwb_ebuf, "Two many arguments to function MID$()" );
      bwb_error( bwb_ebuf );
      return &nvar;
      }

#else
   if ( fnc_checkargs( argc, argv, 2, 3 ) == FALSE )
      {
      return NULL;
      }
#endif

   /* get arguments */

   str_btoc( target_string, var_getsval( &( argv[ 0 ] ) ));
   target_counter = var_getival( &( argv[ 1 ] ) ) - 1;
   if ( target_counter > strlen( target_string ))
      {
      tbuf[ 0 ] = '\0';
      str_ctob( var_findsval( &nvar, nvar.array_pos ), tbuf );
      return &nvar;
      }

   if ( argc == 3 )
      {
      num_spaces = var_getival( &( argv[ 2 ] ));
      }
   else
      {
      num_spaces = MAXSTRINGSIZE;
      }

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in fnc_mid() string <%s> startpos <%d> spaces <%d>",
      target_string, target_counter, num_spaces );
   bwb_debug( bwb_ebuf );
#endif

   c = 0;
   tbuf[ c ] = '\0';
   while ( ( c < num_spaces ) && ( target_string[ target_counter ] != '\0' ))
      {
      tbuf[ c ] = target_string[ target_counter ];
      ++c;
      tbuf[ c ] = '\0';
      ++target_counter;
      }
   str_ctob( var_findsval( &nvar, nvar.array_pos ), tbuf );

   return &nvar;
   }

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

        FUNCTION:       fnc_left()

        DESCRIPTION:    This C function implements the BASIC
                        predefined LEFT$ function

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

struct bwb_variable *
fnc_left( int argc, struct bwb_variable *argv  )
   {
   static struct bwb_variable nvar;
   register int c;
   char target_string[ MAXSTRINGSIZE + 1 ];
   int target_counter, num_spaces;
   char tbuf[ MAXSTRINGSIZE + 1 ];
   static int init = FALSE;

   /* initialize the variable if necessary */

   if ( init == FALSE )
      {
      init = TRUE;
      var_make( &nvar, STRING );
      }

   /* check arguments */

#if PROG_ERRORS
   if ( argc < 2 )
      {
      sprintf( bwb_ebuf, "Not enough arguments to function LEFT$()" );
      bwb_error( bwb_ebuf );
      return &nvar;
      }

   if ( argc > 2 )
      {
      sprintf( bwb_ebuf, "Two many arguments to function LEFT$()" );
      bwb_error( bwb_ebuf );
      return &nvar;
      }

#else
   if ( fnc_checkargs( argc, argv, 2, 2 ) == FALSE )
      {
      return NULL;
      }
#endif

   /* get arguments */

   str_btoc( tbuf, var_getsval( &( argv[ 0 ] ) ));
   target_counter = 0;
   num_spaces = var_getival( &( argv[ 1 ] ));

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in fnc_left() string <%s> startpos <%d> spaces <%d>",
      tbuf, target_counter, num_spaces );
   bwb_debug( bwb_ebuf );
#endif

   c = 0;
   target_string[ 0 ] = '\0';
   while (( c < num_spaces ) && ( tbuf[ c ] != '\0' ))
      {
      target_string[ target_counter ] = tbuf[ c ];
      ++target_counter;
      target_string[ target_counter ] = '\0';
      ++c;
      }
   str_ctob( var_findsval( &nvar, nvar.array_pos ), target_string );

   return &nvar;
   }

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

        FUNCTION:       fnc_right()

        DESCRIPTION:    This C function implements the BASIC
                        predefined RIGHT$ function

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

struct bwb_variable *
fnc_right( int argc, struct bwb_variable *argv  )
   {
   static struct bwb_variable nvar;
   register int c;
   char target_string[ MAXSTRINGSIZE + 1 ];
   int target_counter, num_spaces;
   char tbuf[ MAXSTRINGSIZE + 1 ];
   static int init = FALSE;

   /* initialize the variable if necessary */

   if ( init == FALSE )
      {
      init = TRUE;
      var_make( &nvar, STRING );
      }

   /* check arguments */

#if PROG_ERRORS
   if ( argc < 2 )
      {
      sprintf( bwb_ebuf, "Not enough arguments to function RIGHT$()" );
      bwb_error( bwb_ebuf );
      return &nvar;
      }

   if ( argc > 2 )
      {
      sprintf( bwb_ebuf, "Two many arguments to function RIGHT$()" );
      bwb_error( bwb_ebuf );
      return &nvar;
      }

#else
   if ( fnc_checkargs( argc, argv, 2, 2 ) == FALSE )
      {
      return NULL;
      }
#endif

   /* get arguments */

   str_btoc( target_string, var_getsval( &( argv[ 0 ] ) ));
   target_counter = strlen( target_string ) - var_getival( &( argv[ 1 ] ));
   num_spaces = MAXSTRINGSIZE;

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in fnc_right() string <%s> startpos <%d> spaces <%d>",
      target_string, target_counter, num_spaces );
   bwb_debug( bwb_ebuf );
#endif

   c = 0;
   tbuf[ c ] = '\0';
   while ( ( c < num_spaces ) && ( target_string[ target_counter ] != '\0' ))
      {
      tbuf[ c ] = target_string[ target_counter ];
      ++c;
      tbuf[ c ] = '\0';
      ++target_counter;
      }
   str_ctob( var_findsval( &nvar, nvar.array_pos ), tbuf );

   return &nvar;
   }

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

        FUNCTION:       fnc_timer()

        DESCRIPTION:    This C function implements the BASIC
                        predefined TIMER function

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

struct bwb_variable *
fnc_timer( int argc, struct bwb_variable *argv  )
   {
   static struct bwb_variable nvar;
   static time_t now;
   static int init = FALSE;

   /* initialize the variable if necessary */

   if ( init == FALSE )
      {
      init = TRUE;
      var_make( &nvar, SINGLE );
      }

   time( &now );
   * var_findfval( &nvar, nvar.array_pos )
      = (float) fmod( (double) now, (double) (60*60*24));

   return &nvar;
   }

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

        FUNCTION:       fnc_val()

        DESCRIPTION:

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

struct bwb_variable *
fnc_val( int argc, struct bwb_variable *argv )
   {
   static struct bwb_variable nvar;
   static char *tbuf;
   static int init = FALSE;

   /* initialize the variable if necessary */

   if ( init == FALSE )
      {
      init = TRUE;
      var_make( &nvar, SINGLE );
      if ( ( tbuf = calloc( MAXSTRINGSIZE + 1, sizeof( char ) )) == NULL )
         {
         bwb_error( err_getmem );
         }
      }

   /* check arguments */

#if PROG_ERRORS
   if ( argc < 1 )
      {
      sprintf( bwb_ebuf, "Not enough arguments to function VAL()" );
      bwb_error( bwb_ebuf );
      return NULL;
      }
   else if ( argc > 1 )
      {
      sprintf( bwb_ebuf, "Too many parameters (%d) to function VAL().",
         argc );
      bwb_error( bwb_ebuf );
      return NULL;
      }

#else
   if ( fnc_checkargs( argc, argv, 1, 1 ) == FALSE )
      {
      return NULL;
      }
#endif

   else if ( argv[ 0 ].type != STRING )
      {
#if PROG_ERRORS
      sprintf( bwb_ebuf, "Argument to function VAL() must be a string.",
         argc );
      bwb_error( bwb_ebuf );
#else
      bwb_error( err_mismatch );
#endif
      return NULL;
      }

   /* read the value */

   str_btoc( tbuf, var_getsval( &( argv[ 0 ] ) ));
   sscanf( tbuf, "%f",
       var_findfval( &nvar, nvar.array_pos ) );

   return &nvar;
   }

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

        FUNCTION:       fnc_len()

        DESCRIPTION:

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

struct bwb_variable *
fnc_len( int argc, struct bwb_variable *argv )
   {
   static struct bwb_variable nvar;
   static int init = FALSE;
   static char *tbuf;

   /* initialize the variable if necessary */

   if ( init == FALSE )
      {
      init = TRUE;
      var_make( &nvar, INTEGER );
      if ( ( tbuf = calloc( MAXSTRINGSIZE + 1, sizeof( char ) )) == NULL )
         {
         bwb_error( err_getmem );
         }
      }

   /* check parameters */

#if PROG_ERRORS
   if ( argc < 1 )
      {
      sprintf( bwb_ebuf, "Not enough parameters (%d) to function LEN().",
         argc );
      bwb_error( bwb_ebuf );
      return NULL;
      }
   else if ( argc > 1 )
      {
      sprintf( bwb_ebuf, "Too many parameters (%d) to function LEN().",
         argc );
      bwb_error( bwb_ebuf );
      return NULL;
      }
#else
   if ( fnc_checkargs( argc, argv, 1, 1 ) == FALSE )
      {
      return NULL;
      }
#endif

   /* return length as an integer */

   str_btoc( tbuf, var_getsval( &( argv[ 0 ] )) );
   * var_findival( &nvar, nvar.array_pos )
      = strlen( tbuf );

   return &nvar;
   }

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

        FUNCTION:       fnc_hex()

        DESCRIPTION:

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

struct bwb_variable *
fnc_hex( int argc, struct bwb_variable *argv )
   {
   static struct bwb_variable nvar;
   static char *tbuf;
   static int init = FALSE;

   /* initialize the variable if necessary */

   if ( init == FALSE )
      {
      init = TRUE;
      var_make( &nvar, STRING );
      if ( ( tbuf = calloc( MAXSTRINGSIZE + 1, sizeof( char ) )) == NULL )
         {
         bwb_error( err_getmem );
         }
      }

   /* check parameters */

#if PROG_ERRORS
   if ( argc < 1 )
      {
      sprintf( bwb_ebuf, "Not enough parameters (%d) to function HEX$().",
         argc );
      bwb_error( bwb_ebuf );
      return NULL;
      }
   else if ( argc > 1 )
      {
      sprintf( bwb_ebuf, "Too many parameters (%d) to function HEX$().",
         argc );
      bwb_error( bwb_ebuf );
      return NULL;
      }
#else
   if ( fnc_checkargs( argc, argv, 1, 1 ) == FALSE )
      {
      return NULL;
      }
#endif

   /* format as hex integer */

   sprintf( tbuf, "%X", (int) trnc_int( (double) var_getfval( &( argv[ 0 ] )) ) );
   str_ctob( var_findsval( &nvar, nvar.array_pos ), tbuf );
   return &nvar;
   }

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

        FUNCTION:       fnc_oct()

        DESCRIPTION:	This C function implements the BASIC
			OCT$() function.

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

struct bwb_variable *
fnc_oct( int argc, struct bwb_variable *argv )
   {
   static struct bwb_variable nvar;
   static char *tbuf;
   static int init = FALSE;

   /* initialize the variable if necessary */

   if ( init == FALSE )
      {
      init = TRUE;
      var_make( &nvar, STRING );
      if ( ( tbuf = calloc( MAXSTRINGSIZE + 1, sizeof( char ) )) == NULL )
         {
         bwb_error( err_getmem );
         }
      }

   /* check parameters */

#if PROG_ERRORS
   if ( argc < 1 )
      {
      sprintf( bwb_ebuf, "Not enough parameters (%d) to function OCT$().",
         argc );
      bwb_error( bwb_ebuf );
      return NULL;
      }
   else if ( argc > 1 )
      {
      sprintf( bwb_ebuf, "Too many parameters (%d) to function OCT$().",
         argc );
      bwb_error( bwb_ebuf );
      return NULL;
      }
#else
   if ( fnc_checkargs( argc, argv, 1, 1 ) == FALSE )
      {
      return NULL;
      }
#endif

   /* format as octal integer */

   sprintf( tbuf, "%o", var_getival( &( argv[ 0 ] ) ) );
   str_ctob( var_findsval( &nvar, nvar.array_pos ), tbuf );
   return &nvar;
   }

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

        FUNCTION:       fnc_asc()

        DESCRIPTION:    This function implements the predefined
			BASIC ASC() function.

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

struct bwb_variable *
fnc_asc( int argc, struct bwb_variable *argv )
   {
   static struct bwb_variable nvar;
   static char *tbuf;
   static int init = FALSE;

   /* initialize the variable if necessary */

   if ( init == FALSE )
      {
      init = TRUE;
      var_make( &nvar, INTEGER );
      if ( ( tbuf = calloc( MAXSTRINGSIZE + 1, sizeof( char ) )) == NULL )
         {
         bwb_error( err_getmem );
         }
      }

   /* check parameters */

#if PROG_ERRORS
   if ( argc < 1 )
      {
      sprintf( bwb_ebuf, "Not enough parameters (%d) to function ASC().",
         argc );
      bwb_error( bwb_ebuf );
      return NULL;
      }
   else if ( argc > 1 )
      {
      sprintf( bwb_ebuf, "Too many parameters (%d) to function ASC().",
         argc );
      bwb_error( bwb_ebuf );
      return NULL;
      }
#else
   if ( fnc_checkargs( argc, argv, 1, 1 ) == FALSE )
      {
      return NULL;
      }
#endif

   if ( argv[ 0 ].type != STRING )
      {
#if PROG_ERRORS
      sprintf( bwb_ebuf, "Argument to function ASC() must be a string.",
         argc );
      bwb_error( bwb_ebuf );
#else
      bwb_error( err_mismatch );
#endif
      return NULL;
      }

   /* assign ASCII value of first character in the buffer */

   str_btoc( tbuf, var_findsval( &( argv[ 0 ] ), argv[ 0 ].array_pos ) );
   * var_findival( &nvar, nvar.array_pos ) = (int) tbuf[ 0 ];

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

   return &nvar;
   }

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

        FUNCTION:       fnc_string()

        DESCRIPTION:    This C function implements the BASIC
			STRING$() function.

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

struct bwb_variable *
fnc_string( int argc, struct bwb_variable *argv )
   {
   static struct bwb_variable nvar;
   int length;
   register int i;
   char c;
   struct bwb_variable *v;
   static char *tbuf;
   static int init = FALSE;

   /* initialize the variable if necessary */

   if ( init == FALSE )
      {
      init = TRUE;
      var_make( &nvar, STRING );
      if ( ( tbuf = calloc( MAXSTRINGSIZE + 1, sizeof( char ) )) == NULL )
         {
         bwb_error( err_getmem );
         }
      }

   /* check for correct number of parameters */

#if PROG_ERRORS
   if ( argc < 2 )
      {
      sprintf( bwb_ebuf, "Not enough parameters (%d) to function STRING$().",
         argc );
      bwb_error( bwb_ebuf );
      return NULL;
      }
   else if ( argc > 2 )
      {
      sprintf( bwb_ebuf, "Too many parameters (%d) to function STRING$().",
         argc );
      bwb_error( bwb_ebuf );
      return NULL;
      }
#else
   if ( fnc_checkargs( argc, argv, 2, 2 ) == FALSE )
      {
      return NULL;
      }
#endif

   strcpy( nvar.name, "(string$)!" );
   nvar.type = STRING;
   tbuf[ 0 ] = '\0';
   length = var_getival( &( argv[ 0 ] ));

   if ( argv[ 1 ].type == STRING )
      {
      str_btoc( tbuf, var_getsval( &( argv[ 1 ] )));
      c = tbuf[ 0 ];
      }
   else
      {
      c = (char) var_getival( &( argv[ 1 ] ) );
      }

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in fnc_string(): argument <%s> arg type <%c>, length <%d>",
      argv[ 1 ].string, argv[ 1 ].type, length );
   bwb_debug( bwb_ebuf );
   sprintf( bwb_ebuf, "in fnc_string(): type <%c>, c <0x%x>=<%c>",
      argv[ 1 ].type, c, c );
   bwb_debug( bwb_ebuf );
#endif

   /* add characters to the string */

   for ( i = 0; i < length; ++i )
      {
      tbuf[ i ] = c;
      tbuf[ i + 1 ] = '\0';
      }
   str_ctob( var_findsval( &nvar, nvar.array_pos ), tbuf );

   return &nvar;
   }

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

        FUNCTION:       fnc_environ()

        DESCRIPTION:    This C function implements the BASIC
			ENVIRON$() function.

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

struct bwb_variable *
fnc_environ( int argc, struct bwb_variable *argv )
   {
   char tbuf[ MAXSTRINGSIZE + 1 ];
   char tmp[ MAXSTRINGSIZE + 1 ];
   static struct bwb_variable nvar;
   static int init = FALSE;

   /* initialize the variable if necessary */

   if ( init == FALSE )
      {
      init = TRUE;
      var_make( &nvar, STRING );
      }

   /* check for correct number of parameters */

#if PROG_ERRORS
   if ( argc < 1 )
      {
      sprintf( bwb_ebuf, "Not enough parameters (%d) to function ENVIRON$().",
         argc );
      bwb_error( bwb_ebuf );
      return NULL;
      }
   else if ( argc > 1 )
      {
      sprintf( bwb_ebuf, "Too many parameters (%d) to function ENVIRON$().",
         argc );
      bwb_error( bwb_ebuf );
      return NULL;
      }
#else
   if ( fnc_checkargs( argc, argv, 1, 1 ) == FALSE )
      {
      return NULL;
      }
#endif

   /* resolve the argument and place string value in tbuf */

   str_btoc( tbuf, var_getsval( &( argv[ 0 ] )));

   /* call getenv() then write value to string */

   strcpy( tmp, getenv( tbuf ));
   str_ctob( var_findsval( &nvar, nvar.array_pos ), tmp );

   /* return address of nvar */

   return &nvar;

   }

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

        FUNCTION:       fnc_instr()

        DESCRIPTION:    

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

struct bwb_variable *
fnc_instr( int argc, struct bwb_variable *argv )
   {
   static struct bwb_variable nvar;
   static int init = FALSE;
   int n_pos, x_pos, y_pos;
   int start_pos;
   register int n;
   char xbuf[ MAXSTRINGSIZE + 1 ];
   char ybuf[ MAXSTRINGSIZE + 1 ];

   /* initialize the variable if necessary */

   if ( init == FALSE )
      {
      init = TRUE;
      var_make( &nvar, INTEGER );
      }

   /* check for correct number of parameters */

#if PROG_ERRORS
   if ( argc < 2 )
      {
      sprintf( bwb_ebuf, "Not enough parameters (%d) to function INSTR().",
         argc );
      bwb_error( bwb_ebuf );
      return NULL;
      }
   else if ( argc > 3 )
      {
      sprintf( bwb_ebuf, "Too many parameters (%d) to function INSTR().",
         argc );
      bwb_error( bwb_ebuf );
      return NULL;
      }
#else
   if ( fnc_checkargs( argc, argv, 2, 3 ) == FALSE )
      {
      return NULL;
      }
#endif

   /* determine argument positions */

   if ( argc == 3 )
      {
      n_pos = 0;
      x_pos = 1;
      y_pos = 2;
      }
   else
      {
      n_pos = -1;
      x_pos = 0;
      y_pos = 1;
      }

   /* determine starting position */

   if ( n_pos == 0 )
      {
      start_pos = var_getival( &( argv[ n_pos ] ) ) - 1;
      }
   else
      {
      start_pos = 0;
      }

   /* get x and y strings */

   str_btoc( xbuf, var_getsval( &( argv[ x_pos ] ) ) );
   str_btoc( ybuf, var_getsval( &( argv[ y_pos ] ) ) );

   /* now search for match */

   for ( n = start_pos; n < strlen( xbuf ); ++n )
      {
      if ( strncmp( &( xbuf[ n ] ), ybuf, strlen( ybuf ) ) == 0 )
         {
         * var_findival( &nvar, nvar.array_pos ) = n + 1;
         return &nvar;
         }
      }

   /* match not found */
      
   * var_findival( &nvar, nvar.array_pos ) = 0;
   return &nvar;

   }

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

        FUNCTION:       fnc_str()

        DESCRIPTION:    

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

struct bwb_variable *
fnc_str( int argc, struct bwb_variable *argv )
   {
   static struct bwb_variable nvar;
   static char *tbuf;
   static int init = FALSE;

   /* initialize the variable if necessary */

   if ( init == FALSE )
      {
      init = TRUE;
      var_make( &nvar, STRING );
      if ( ( tbuf = calloc( MAXSTRINGSIZE + 1, sizeof( char ) )) == NULL )
         {
         bwb_error( err_getmem );
         }
      }

   /* check parameters */

#if PROG_ERRORS
   if ( argc < 1 )
      {
      sprintf( bwb_ebuf, "Not enough parameters (%d) to function STR$().",
         argc );
      bwb_error( bwb_ebuf );
      return NULL;
      }
   else if ( argc > 1 )
      {
      sprintf( bwb_ebuf, "Too many parameters (%d) to function STR$().",
         argc );
      bwb_error( bwb_ebuf );
      return NULL;
      }
#else
   if ( fnc_checkargs( argc, argv, 1, 1 ) == FALSE )
      {
      return NULL;
      }
#endif

   /* format as decimal number */

   sprintf( tbuf, " %.*f", prn_precision( &( argv[ 0 ] ) ), 
      var_getfval( &( argv[ 0 ] ) ) ); 
   str_ctob( var_findsval( &nvar, nvar.array_pos ), tbuf );

   return &nvar;
   }

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

        FUNCTION:       fnc_checkargs()

        DESCRIPTION:    This C function checks the arguments to
			functions.

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

#if PROG_ERRORS
#else
int
fnc_checkargs( int argc, struct bwb_variable *argv, int min, int max )
   {

   if ( argc < min )
      {
      bwb_error( err_syntax );
      return FALSE;
      }
   if ( argc > max )
      {
      bwb_error( err_syntax );
      return FALSE;
      }

   return TRUE;

   }
#endif

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

        FUNCTION:       fnc_fncs()

        DESCRIPTION:    This C function is used for debugging
                        purposes; it prints a list of all defined
                        functions.

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

#if PERMANENT_DEBUG
struct bwb_line *
bwb_fncs( struct bwb_line *l )
   {
   struct bwb_function *f;

   for ( f = fnc_start.next; f != &fnc_end; f = f->next )
      {
      fprintf( stdout, "%s\t%c \n", f->name, f->type );
      }

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

   }
#endif
