/****************************************************************************
 * apps/interpreters/bas/bas_statement.c
 *
 *   Copyright (c) 1999-2014 Michael Haardt
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 * Adapted to NuttX and re-released under a 3-clause BSD license:
 *
 *   Copyright (C) 2014 Gregory Nutt. All rights reserved.
 *   Authors: Alan Carvalho de Assis <Alan Carvalho de Assis>
 *            Gregory Nutt <gnutt@nuttx.org>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name NuttX nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <nuttx/config.h>

#include <stdlib.h>

#include "bas_statement.h"

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

#define _(String) String

/****************************************************************************
 * Private Functions
 ****************************************************************************/

/****************************************************************************
 * Public Functions
 ****************************************************************************/

struct Value *stmt_CALL(struct Value *value)
{
  ++g_pc.token;
  if (g_pc.token->type != T_IDENTIFIER)
    {
      return Value_new_ERROR(value, MISSINGPROCIDENT);
    }

  if (g_pass == DECLARE)
    {
      if (func(value)->type == V_ERROR)
        {
          return value;
        }
      else
        {
          Value_destroy(value);
        }
    }
  else
    {
      if (g_pass == COMPILE)
        {
          if (Global_find
              (&g_globals, g_pc.token->u.identifier,
               (g_pc.token + 1)->type == T_OP) == 0)
            {
              return Value_new_ERROR(value, UNDECLARED);
            }
        }

      if (g_pc.token->u.identifier->sym->type != USERFUNCTION &&
          g_pc.token->u.identifier->sym->type != BUILTINFUNCTION)
        {
          return Value_new_ERROR(value, TYPEMISMATCH1, "variable", "function");
        }

      func(value);
      if (Value_retype(value, V_VOID)->type == V_ERROR)
        {
          return value;
        }

      Value_destroy(value);
    }

  return (struct Value *)0;
}

struct Value *stmt_CASE(struct Value *value)
{
  struct Pc statementpc = g_pc;

  if (g_pass == DECLARE || g_pass == COMPILE)
    {
      struct Pc *selectcase, *nextcasevalue;

      if ((selectcase = findLabel(L_SELECTCASE)) == (struct Pc *)0)
        {
          return Value_new_ERROR(value, STRAYCASE);
        }

      for (nextcasevalue = &selectcase->token->u.selectcase->nextcasevalue;
           nextcasevalue->line != -1;
           nextcasevalue = &nextcasevalue->token->u.casevalue->nextcasevalue);

      *nextcasevalue = g_pc;
      if (g_pass == COMPILE)
        {
          g_pc.token->u.casevalue->endselect =
            selectcase->token->u.selectcase->endselect;
        }

      g_pc.token->u.casevalue->nextcasevalue.line = -1;
      ++g_pc.token;
      switch (statementpc.token->type)
        {
        case T_CASEELSE:
          break;

        case T_CASEVALUE:
          {
            struct Pc exprpc;

            do
              {
                if (g_pc.token->type == T_IS)
                  {
                    ++g_pc.token;
                    switch (g_pc.token->type)
                      {
                      case T_LT:
                      case T_LE:
                      case T_EQ:
                      case T_GE:
                      case T_GT:
                      case T_NE:
                        break;

                      default:
                        return Value_new_ERROR(value, MISSINGRELOP);
                      }

                    ++g_pc.token;
                    exprpc = g_pc;
                    if (eval(value, "`is'")->type == V_ERROR)
                      {
                        return value;
                      }

                    if (Value_retype
                        (value,
                         selectcase->token->u.selectcase->type)->type ==
                        V_ERROR)
                      {
                        g_pc = exprpc;
                        return value;
                      }

                    Value_destroy(value);
                  }

                else            /* value or range */
                  {
                    exprpc = g_pc;
                    if (eval(value, "`case'")->type == V_ERROR)
                      {
                        return value;
                      }

                    if (Value_retype
                        (value,
                         selectcase->token->u.selectcase->type)->type ==
                        V_ERROR)
                      {
                        g_pc = exprpc;
                        return value;
                      }

                    Value_destroy(value);
                    if (g_pc.token->type == T_TO)
                      {
                        ++g_pc.token;
                        exprpc = g_pc;
                        if (eval(value, "`case'")->type == V_ERROR)
                          {
                            return value;
                          }

                        if (Value_retype
                            (value,
                             selectcase->token->u.selectcase->type)->type ==
                            V_ERROR)
                          {
                            g_pc = exprpc;
                            return value;
                          }

                        Value_destroy(value);
                      }

                  }

                if (g_pc.token->type == T_COMMA)
                  {
                    ++g_pc.token;
                  }
                else
                  {
                    break;
                  }
              }
            while (1);

            break;
          }

        default:
          assert(0);
        }
    }
  else
    {
      g_pc = g_pc.token->u.casevalue->endselect;
    }

  return (struct Value *)0;
}

struct Value *stmt_CHDIR_MKDIR(struct Value *value)
{
  struct Pc dirpc;
  struct Pc statementpc = g_pc;
  int res = -1;
  int errcode = -1;

  ++g_pc.token;
  dirpc = g_pc;
  if (eval(value, _("directory"))->type == V_ERROR ||
      Value_retype(value, V_STRING)->type == V_ERROR)
    {
      return value;
    }

  if (g_pass == INTERPRET)
    {
      switch (statementpc.token->type)
        {
        case T_CHDIR:
          res = chdir(value->u.string.character);
          break;

        case T_MKDIR:
          res = mkdir(value->u.string.character, 0777);
          break;

        default:
          assert(0);
        }

      errcode = errno;
    }

  Value_destroy(value);
  if (g_pass == INTERPRET && res == -1)
    {
      g_pc = dirpc;
      return Value_new_ERROR(value, IOERROR, strerror(errcode));
    }

  return (struct Value *)0;
}

struct Value *stmt_CLEAR(struct Value *value)
{
  if (g_pass == INTERPRET)
    {
      Global_clear(&g_globals);
      FS_closefiles();
    }

  ++g_pc.token;
  return (struct Value *)0;
}

struct Value *stmt_CLOSE(struct Value *value)
{
  int hasargs = 0;
  struct Pc chnpc;

  ++g_pc.token;
  while (1)
    {
      chnpc = g_pc;
      if (g_pc.token->type == T_CHANNEL)
        {
          hasargs = 1;
          ++g_pc.token;
        }

      if (eval(value, (const char *)0) == (struct Value *)0)
        {
          if (hasargs)
            {
              return Value_new_ERROR(value, MISSINGEXPR, _("channel"));
            }
          else
            {
              break;
            }
        }

      hasargs = 1;
      if (value->type == V_ERROR ||
          Value_retype(value, V_INTEGER)->type == V_ERROR)
        {
          return value;
        }

      if (g_pass == INTERPRET && FS_close(value->u.integer) == -1)
        {
          Value_destroy(value);
          g_pc = chnpc;
          return Value_new_ERROR(value, IOERROR, FS_errmsg);
        }

      if (g_pc.token->type == T_COMMA)
        {
          ++g_pc.token;
        }
      else
        {
          break;
        }
    }

  if (!hasargs && g_pass == INTERPRET)
    {
      FS_closefiles();
    }

  return (struct Value *)0;
}

struct Value *stmt_CLS(struct Value *value)
{
  struct Pc statementpc = g_pc;

  ++g_pc.token;
  if (g_pass == INTERPRET && FS_cls(STDCHANNEL) == -1)
    {
      g_pc = statementpc;
      return Value_new_ERROR(value, IOERROR, FS_errmsg);
    }

  return (struct Value *)0;
}

struct Value *stmt_COLOR(struct Value *value)
{
  int foreground = -1, background = -1;
  struct Pc statementpc = g_pc;

  ++g_pc.token;
  if (eval(value, (const char *)0))
    {
      if (value->type == V_ERROR ||
          (g_pass != DECLARE && Value_retype(value, V_INTEGER)->type == V_ERROR))
        {
          return value;
        }

      foreground = value->u.integer;
      if (foreground < 0 || foreground > 15)
        {
          Value_destroy(value);
          g_pc = statementpc;
          return Value_new_ERROR(value, OUTOFRANGE, _("foreground colour"));
        }
    }

  Value_destroy(value);
  if (g_pc.token->type == T_COMMA)
    {
      ++g_pc.token;
      if (eval(value, (const char *)0))
        {
          if (value->type == V_ERROR ||
              (g_pass != DECLARE &&
               Value_retype(value, V_INTEGER)->type == V_ERROR))
            {
              return value;
            }

          background = value->u.integer;
          if (background < 0 || background > 15)
            {
              Value_destroy(value);
              g_pc = statementpc;
              return Value_new_ERROR(value, OUTOFRANGE, _("background colour"));
            }
        }

      Value_destroy(value);
      if (g_pc.token->type == T_COMMA)
        {
          ++g_pc.token;
          if (eval(value, (const char *)0))
            {
              int bordercolour = -1;

              if (value->type == V_ERROR ||
                  (g_pass != DECLARE &&
                   Value_retype(value, V_INTEGER)->type == V_ERROR))
                {
                  return value;
                }

              bordercolour = value->u.integer;
              if (bordercolour < 0 || bordercolour > 15)
                {
                  Value_destroy(value);
                  g_pc = statementpc;
                  return Value_new_ERROR(value, OUTOFRANGE, _("border colour"));
                }
            }

          Value_destroy(value);
        }
    }

  if (g_pass == INTERPRET)
    {
      FS_colour(STDCHANNEL, foreground, background);
    }

  return (struct Value *)0;
}

struct Value *stmt_DATA(struct Value *value)
{
  if (DIRECTMODE)
    {
      return Value_new_ERROR(value, NOTINDIRECTMODE);
    }

  if (g_pass == DECLARE)
    {
      *g_lastdata = g_pc;
      (g_lastdata = &(g_pc.token->u.nextdata))->line = -1;
    }

  ++g_pc.token;
  while (1)
    {
      if (g_pc.token->type != T_STRING && g_pc.token->type != T_DATAINPUT)
        {
          return Value_new_ERROR(value, MISSINGDATAINPUT);
        }

      ++g_pc.token;
      if (g_pc.token->type != T_COMMA)
        {
          break;
        }
      else
        {
          ++g_pc.token;
        }
    }

  return (struct Value *)0;
}

struct Value *stmt_DEFFN_DEFPROC_FUNCTION_SUB(struct Value *value)
{
  if (g_pass == DECLARE || g_pass == COMPILE)
    {
      struct Pc statementpc = g_pc;
      struct Identifier *fn;
      int proc;
      int args = 0;

      if (DIRECTMODE)
        {
          return Value_new_ERROR(value, NOTINDIRECTMODE);
        }

      proc = (g_pc.token->type == T_DEFPROC || g_pc.token->type == T_SUB);
      ++g_pc.token;
      if (g_pc.token->type != T_IDENTIFIER)
        {
          if (proc)
            {
              return Value_new_ERROR(value, MISSINGPROCIDENT);
            }
          else
            {
              return Value_new_ERROR(value, MISSINGFUNCIDENT);
            }
        }

      fn = g_pc.token->u.identifier;
      if (proc)
        {
          fn->defaultType = V_VOID;
        }

      ++g_pc.token;
      if (findLabel(L_FUNC))
        {
          g_pc = statementpc;
          return Value_new_ERROR(value, NESTEDDEFINITION);
        }

      Auto_variable(&g_stack, fn);
      if (g_pc.token->type == T_OP)       /* arguments */
        {
          ++g_pc.token;
          while (1)
            {
              if (g_pc.token->type != T_IDENTIFIER)
                {
                  Auto_funcEnd(&g_stack);
                  return Value_new_ERROR(value, MISSINGFORMIDENT);
                }

              if (Auto_variable(&g_stack, g_pc.token->u.identifier) == 0)
                {
                  Auto_funcEnd(&g_stack);
                  return Value_new_ERROR(value, ALREADYDECLARED);
                }

              ++args;
              ++g_pc.token;
              if (g_pc.token->type == T_COMMA)
                {
                  ++g_pc.token;
                }
              else
                {
                  break;
                }
            }

          if (g_pc.token->type != T_CP)
            {
              Auto_funcEnd(&g_stack);
              return Value_new_ERROR(value, MISSINGCP);
            }

          ++g_pc.token;
        }

      if (g_pass == DECLARE)
        {
          enum ValueType *t =
            args ? malloc(args * sizeof(enum ValueType)) : (enum ValueType *)0;
          int i;

          for (i = 0; i < args; ++i)
            {
              t[i] = Auto_argType(&g_stack, i);
            }

          if (Global_function
              (&g_globals, fn, fn->defaultType, &g_pc, &statementpc, args, t) == 0)
            {
              free(t);
              Auto_funcEnd(&g_stack);
              g_pc = statementpc;
              return Value_new_ERROR(value, REDECLARATION);
            }

          Program_addScope(&g_program, &fn->sym->u.sub.u.def.scope);
        }

      pushLabel(L_FUNC, &statementpc);
      if (g_pc.token->type == T_EQ)
        {
          return stmt_EQ_FNRETURN_FNEND(value);
        }
    }
  else
    {
      g_pc = (g_pc.token + 1)->u.identifier->sym->u.sub.u.def.scope.end;
    }

  return (struct Value *)0;
}

struct Value *stmt_DEC_INC(struct Value *value)
{
  int step;

  step = (g_pc.token->type == T_DEC ? -1 : 1);
  ++g_pc.token;
  while (1)
    {
      struct Value *l, stepValue;
      struct Pc lvaluepc;

      lvaluepc = g_pc;
      if (g_pc.token->type != T_IDENTIFIER)
        {
          return Value_new_ERROR(value, MISSINGDECINCIDENT);
        }

      if (g_pass == DECLARE &&
          Global_variable(&g_globals, g_pc.token->u.identifier,
                          g_pc.token->u.identifier->defaultType,
                          (g_pc.token + 1)->type ==
                          T_OP ? GLOBALARRAY : GLOBALVAR, 0) == 0)
        {
          return Value_new_ERROR(value, REDECLARATION);
        }

      if ((l = lvalue(value))->type == V_ERROR)
        {
          return value;
        }

      if (l->type == V_INTEGER)
        {
          VALUE_NEW_INTEGER(&stepValue, step);
        }
      else if (l->type == V_REAL)
        {
          VALUE_NEW_REAL(&stepValue, (double)step);
        }
      else
        {
          g_pc = lvaluepc;
          return Value_new_ERROR(value, TYPEMISMATCH5);
        }

      if (g_pass == INTERPRET)
        {
          Value_add(l, &stepValue, 1);
        }

      Value_destroy(&stepValue);
      if (g_pc.token->type == T_COMMA)
        {
          ++g_pc.token;
        }
      else
        {
          break;
        }
    }

  return (struct Value *)0;
}

struct Value *stmt_DEFINT_DEFDBL_DEFSTR(struct Value *value)
{
  enum ValueType dsttype = V_NIL;

  switch (g_pc.token->type)
    {
    case T_DEFINT:
      dsttype = V_INTEGER;
      break;

    case T_DEFDBL:
      dsttype = V_REAL;
      break;

    case T_DEFSTR:
      dsttype = V_STRING;
      break;

    default:
      assert(0);
    }

  ++g_pc.token;
  while (1)
    {
      struct Identifier *ident;

      if (g_pc.token->type != T_IDENTIFIER)
        {
          return Value_new_ERROR(value, MISSINGVARIDENT);
        }

      if (g_pc.token->u.identifier->defaultType != V_REAL)
        {
          switch (dsttype)
            {
            case V_INTEGER:
              return Value_new_ERROR(value, BADIDENTIFIER, _("integer"));

            case V_REAL:
              return Value_new_ERROR(value, BADIDENTIFIER, _("real"));

            case V_STRING:
              return Value_new_ERROR(value, BADIDENTIFIER, _("string"));

            default:
              assert(0);
            }
        }

      ident = g_pc.token->u.identifier;
      ++g_pc.token;
      if (g_pc.token->type == T_MINUS)
        {
          struct Identifier i;

          if (strlen(ident->name) != 1)
            {
              return Value_new_ERROR(value, BADRANGE);
            }

          ++g_pc.token;
          if (g_pc.token->type != T_IDENTIFIER)
            {
              return Value_new_ERROR(value, MISSINGVARIDENT);
            }

          if (strlen(g_pc.token->u.identifier->name) != 1)
            {
              return Value_new_ERROR(value, BADRANGE);
            }

          for (i.name[0] = tolower(ident->name[0]), i.name[1] = '\0';
               i.name[0] <= tolower(g_pc.token->u.identifier->name[0]);
               ++i.name[0])
            {
              Global_variable(&g_globals, &i, dsttype, GLOBALVAR, 1);
            }

          ++g_pc.token;
        }
      else
        {
          Global_variable(&g_globals, ident, dsttype, GLOBALVAR, 1);
        }

      if (g_pc.token->type == T_COMMA)
        {
          ++g_pc.token;
        }
      else
        {
          break;
        }
    }

  return (struct Value *)0;
}

struct Value *stmt_DELETE(struct Value *value)
{
  struct Pc from, to;
  int f = 0, t = 0;

  if (g_pass == INTERPRET && !DIRECTMODE)
    {
      return Value_new_ERROR(value, NOTINPROGRAMMODE);
    }

  ++g_pc.token;
  if (g_pc.token->type == T_INTEGER)
    {
      if (g_pass == INTERPRET &&
          Program_goLine(&g_program, g_pc.token->u.integer,
                         &from) == (struct Pc *)0)
        {
          return Value_new_ERROR(value, NOSUCHLINE);
        }

      f = 1;
      ++g_pc.token;
    }

  if (g_pc.token->type == T_MINUS || g_pc.token->type == T_COMMA)
    {
      ++g_pc.token;
      if (g_pc.token->type == T_INTEGER)
        {
          if (g_pass == INTERPRET &&
              Program_goLine(&g_program, g_pc.token->u.integer,
                             &to) == (struct Pc *)0)
            {
              return Value_new_ERROR(value, NOSUCHLINE);
            }

          t = 1;
          ++g_pc.token;
        }
    }
  else if (f == 1)
    {
      to = from;
      t  = 1;
    }

  if (!f && !t)
    {
      return Value_new_ERROR(value, MISSINGLINENUMBER);
    }

  if (g_pass == INTERPRET)
    {
      Program_delete(&g_program, f ? &from : (struct Pc *)0,
                     t ? &to : (struct Pc *)0);
    }

  return (struct Value *)0;
}

struct Value *stmt_DIM(struct Value *value)
{
  ++g_pc.token;
  while (1)
    {
      unsigned int capacity = 0, *geometry = (unsigned int *)0;
      struct Var *var;
      struct Pc dimpc;
      unsigned int dim;
      enum ValueType vartype;

      if (g_pc.token->type != T_IDENTIFIER)
        {
          return Value_new_ERROR(value, MISSINGARRIDENT);
        }

      if (g_pass == DECLARE &&
          Global_variable(&g_globals, g_pc.token->u.identifier,
                          g_pc.token->u.identifier->defaultType, GLOBALARRAY,
                          0) == 0)
        {
          return Value_new_ERROR(value, REDECLARATION);
        }

      var = &g_pc.token->u.identifier->sym->u.var;
      if (g_pass == INTERPRET && var->dim)
        {
          return Value_new_ERROR(value, REDIM);
        }

      vartype = var->type;
      ++g_pc.token;
      if (g_pc.token->type != T_OP)
        {
          return Value_new_ERROR(value, MISSINGOP);
        }

      ++g_pc.token;
      dim = 0;
      while (1)
        {
          dimpc = g_pc;
          if (eval(value, _("dimension"))->type == V_ERROR ||
              (g_pass != DECLARE &&
               Value_retype(value, V_INTEGER)->type == V_ERROR))
            {
              if (capacity)
                {
                  free(geometry);
                }

              return value;
            }

          if (g_pass == INTERPRET && value->u.integer < g_optionbase)       /* error */
            {
              Value_destroy(value);
              Value_new_ERROR(value, OUTOFRANGE, _("dimension"));
            }

          if (value->type == V_ERROR)   /* abort */
            {
              if (capacity)
                {
                  free(geometry);
                }

              g_pc = dimpc;
              return value;
            }

          if (g_pass == INTERPRET)
            {
              if (dim == capacity)      /* enlarge geometry */
                {
                  unsigned int *more;

                  more =
                    realloc(geometry,
                            sizeof(unsigned int) *
                            (capacity ? (capacity *= 2) : (capacity = 3)));
                  geometry = more;
                }

              geometry[dim] = value->u.integer - g_optionbase + 1;
              ++dim;
            }

          Value_destroy(value);
          if (g_pc.token->type == T_COMMA)
            {
              ++g_pc.token;
            }
          else
            {
              break;
            }
        }

      if (g_pc.token->type != T_CP)       /* abort */
        {
          if (capacity)
            {
              free(geometry);
            }

          return Value_new_ERROR(value, MISSINGCP);
        }

      ++g_pc.token;
      if (g_pass == INTERPRET)
        {
          struct Var newarray;

          assert(capacity);
          if (Var_new(&newarray, vartype, dim, geometry, g_optionbase) ==
              (struct Var *)0)
            {
              free(geometry);
              return Value_new_ERROR(value, OUTOFMEMORY);
            }

          Var_destroy(var);
          *var = newarray;
          free(geometry);
        }

      if (g_pc.token->type == T_COMMA)
        {
          ++g_pc.token;           /* advance to next var */
        }
      else
        {
          break;
        }
    }

  return (struct Value *)0;
}

struct Value *stmt_DISPLAY(struct Value *value)
{
  struct Pc statementpc = g_pc;

  ++g_pc.token;
  if (eval(value, _("file name"))->type == V_ERROR ||
      (g_pass != DECLARE && Value_retype(value, V_STRING)->type == V_ERROR))
    {
      return value;
    }

  if (g_pass == INTERPRET && cat(value->u.string.character) == -1)
    {
      const char *msg = strerror(errno);

      Value_destroy(value);
      g_pc = statementpc;
      return Value_new_ERROR(value, IOERROR, msg);
    }
  else
    {
      Value_destroy(value);
    }

  return (struct Value *)0;
}

struct Value *stmt_DO(struct Value *value)
{
  if (g_pass == DECLARE || g_pass == COMPILE)
    {
      pushLabel(L_DO, &g_pc);
    }

  ++g_pc.token;
  return (struct Value *)0;
}

struct Value *stmt_DOcondition(struct Value *value)
{
  struct Pc dowhilepc = g_pc;
  int negate = (g_pc.token->type == T_DOUNTIL);

  if (g_pass == DECLARE || g_pass == COMPILE)
    {
      pushLabel(L_DOcondition, &g_pc);
    }

  ++g_pc.token;
  if (eval(value, "condition")->type == V_ERROR)
    {
      return value;
    }

  if (g_pass == INTERPRET)
    {
      int condition;

      condition = Value_isNull(value);
      if (negate)
        {
          condition = !condition;
        }

      if (condition)
        {
          g_pc = dowhilepc.token->u.exitdo;
        }

      Value_destroy(value);
    }

  return (struct Value *)0;
}

struct Value *stmt_EDIT(struct Value *value)
{
#if defined(CONFIG_EXAMPLES_BAS_EDITOR) && defined(CONFIG_EXAMPLES_BAS_SHELL) && defined(CONFIG_ARCH_HAVE_VFORK)
  long int line;
  struct Pc statementpc = g_pc;
  int status;

  ++g_pc.token;
  if (g_pc.token->type == T_INTEGER)
    {
      struct Pc where;

      if (g_program.numbered)
        {
          if (Program_goLine(&g_program, g_pc.token->u.integer, &where) ==
              (struct Pc *)0)
            {
              return Value_new_ERROR(value, NOSUCHLINE);
            }

          line = where.line + 1;
        }
      else
        {
          if (!Program_end(&g_program, &where))
            {
              return Value_new_ERROR(value, NOPROGRAM);
            }

          line = g_pc.token->u.integer;
          if (line < 1 || line > (where.line + 1))
            {
              return Value_new_ERROR(value, NOSUCHLINE);
            }
        }
      ++g_pc.token;
    }
  else
    {
      line = 1;
    }

  if (g_pass == INTERPRET)
    {
      /* variables */

      char *name;
      int chn;
      struct Program newProgram;
      const char *visual, *basename, *shell;
      struct String cmd;
      static struct
        {
          const char *editor, *flag;
        }
      gotoLine[] =
      {
        {
        "Xemacs", "+%ld "},
        {
        "cemacs", "+%ld "},
        {
        "emacs", "+%ld "},
        {
        "emori", "-l%ld "},
        {
        "fe", "-l%ld "},
        {
        "jed", "+%ld "},
        {
        "jmacs", "+%ld "},
        {
        "joe", "+%ld "},
        {
        "modeori", "-l%ld "},
        {
        "origami", "-l%ld "},
        {
        "vi", "-c%ld "},
        {
        "vim", "+%ld "},
        {
        "xemacs", "+%ld "}
      };
      unsigned int i;
      pid_t pid;

      if (!DIRECTMODE)
        {
          g_pc = statementpc;
          return Value_new_ERROR(value, NOTINPROGRAMMODE);
        }

      if ((name = tmpnam(NULL)) == (char *)0)
        {
          g_pc = statementpc;
          return Value_new_ERROR(value, IOERROR,
                                 _("generating temporary file name failed"));
        }

      if ((chn = FS_openout(name)) == -1)
        {
          g_pc = statementpc;
          return Value_new_ERROR(value, IOERRORCREATE, name, FS_errmsg);
        }

      FS_width(chn, 0);
      if (Program_list(&g_program, chn, 0, (struct Pc *)0, (struct Pc *)0, value))
        {
          g_pc = statementpc;
          return value;
        }

      if (FS_close(chn) == -1)
        {
          g_pc = statementpc;
          unlink(name);
          return Value_new_ERROR(value, IOERRORCLOSE, name, FS_errmsg);
        }

      if ((visual = getenv("VISUAL")) == (char *)0 &&
          (visual = getenv("EDITOR")) == (char *)0)
        {
          visual = "vi";
        }

      basename = strrchr(visual, '/');
      if (basename == (char *)0)
        {
          basename = visual;
        }

      if ((shell = getenv("SHELL")) == (char *)0)
        {
          shell = "/bin/sh";
        }

      String_new(&cmd);
      String_appendChars(&cmd, visual);
      String_appendChar(&cmd, ' ');
      for (i = 0; i < sizeof(gotoLine) / sizeof(gotoLine[0]); ++i)
        {
          if (strcmp(basename, gotoLine[i].editor) == 0)
            {
              String_appendPrintf(&cmd, gotoLine[i].flag, line);
              break;
            }
        }

      String_appendChars(&cmd, name);
      FS_shellmode(STDCHANNEL);
      switch (pid = vfork())
        {
        case -1:
          {
            unlink(name);
            FS_fsmode(STDCHANNEL);
            return Value_new_ERROR(value, FORKFAILED, strerror(errno));
          }

        case 0:
          {
            execl(shell, shell, "-c", cmd.character, (const char *)0);
            exit(127);
          }

        default:
          {
            /* Wait for the editor to complete */

            while (waitpid(pid, &status, 0) < 0 && errno != EINTR);
          }
        }

      FS_fsmode(STDCHANNEL);
      String_destroy(&cmd);
      if ((chn = FS_openin(name)) == -1)
        {
          g_pc = statementpc;
          return Value_new_ERROR(value, IOERROROPEN, name, FS_errmsg);
        }

      Program_new(&newProgram);
      if (Program_merge(&newProgram, chn, value))
        {
          FS_close(chn);
          unlink(name);
          g_pc = statementpc;
          return value;
        }

      FS_close(chn);
      Program_setname(&newProgram, g_program.name.character);
      Program_destroy(&g_program);
      g_program = newProgram;
      unlink(name);
    }

  return (struct Value *)0;
#else
  return Value_new_ERROR(value, NOTAVAILABLE, strerror(ENOSYS));
#endif
}

struct Value *stmt_ELSE_ELSEIFELSE(struct Value *value)
{
  if (g_pass == INTERPRET)
    {
      g_pc = g_pc.token->u.endifpc;
    }

  if (g_pass == DECLARE || g_pass == COMPILE)
    {
      struct Pc elsepc = g_pc;
      struct Pc *ifinstr;
      int elseifelse = (g_pc.token->type == T_ELSEIFELSE);

      if ((ifinstr = popLabel(L_IF)) == (struct Pc *)0)
        {
          return Value_new_ERROR(value, STRAYELSE1);
        }

      if (ifinstr->token->type == T_ELSEIFIF)
        {
          (ifinstr->token - 1)->u.elsepc = g_pc;
        }

      ++g_pc.token;
      ifinstr->token->u.elsepc = g_pc;
      assert(ifinstr->token->type == T_ELSEIFIF ||
             ifinstr->token->type == T_IF);
      if (elseifelse)
        {
          return &more_statements;
        }
      else
        {
          pushLabel(L_ELSE, &elsepc);
        }
    }
  return (struct Value *)0;
}

struct Value *stmt_END(struct Value *value)
{
  if (g_pass == INTERPRET)
    {
      g_pc      = g_pc.token->u.endpc;
      g_bas_end = true;
    }

  if (g_pass == DECLARE || g_pass == COMPILE)
    {
      if (Program_end(&g_program, &g_pc.token->u.endpc))
        {
          ++g_pc.token;
        }
      else
        {
          struct Token *eol;

          for (eol = g_pc.token; eol->type != T_EOL; ++eol);

          g_pc.token->u.endpc = g_pc;
          g_pc.token->u.endpc.token = eol;
          ++g_pc.token;
        }
    }

  return (struct Value *)0;
}

struct Value *stmt_ENDIF(struct Value *value)
{
  if (g_pass == DECLARE || g_pass == COMPILE)
    {
      struct Pc endifpc = g_pc;
      struct Pc *ifpc;
      struct Pc *elsepc;

      if ((ifpc = popLabel(L_IF)))
        {
          ifpc->token->u.elsepc = endifpc;
          if (ifpc->token->type == T_ELSEIFIF)
            {
              (ifpc->token - 1)->u.elsepc = g_pc;
            }
        }
      else if ((elsepc = popLabel(L_ELSE)))
        {
          elsepc->token->u.endifpc = endifpc;
        }
      else
        {
          return Value_new_ERROR(value, STRAYENDIF);
        }
    }

  ++g_pc.token;
  return (struct Value *)0;
}

struct Value *stmt_ENDFN(struct Value *value)
{
  struct Pc *curfn = (struct Pc *)0;
  struct Pc eqpc = g_pc;

  if (g_pass == DECLARE || g_pass == COMPILE)
    {
      if ((curfn = popLabel(L_FUNC)) == (struct Pc *)0)
        {
          return Value_new_ERROR(value, STRAYENDFN);
        }

      if ((eqpc.token->u.type =
           (curfn->token + 1)->u.identifier->defaultType) == V_VOID)
        {
          return Value_new_ERROR(value, STRAYENDFN);
        }
    }

  ++g_pc.token;
  if (g_pass == INTERPRET)
    {
      return Value_clone(value,
                         Var_value(Auto_local(&g_stack, 0), 0, (int *)0,
                                   (struct Value *)0));
    }
  else
    {
      if (g_pass == DECLARE)
        {
          Global_endfunction(&g_globals, (curfn->token + 1)->u.identifier, &g_pc);
        }
      Auto_funcEnd(&g_stack);
    }

  return (struct Value *)0;
}

struct Value *stmt_ENDPROC_SUBEND(struct Value *value)
{
  struct Pc *curfn = (struct Pc *)0;

  if (g_pass == DECLARE || g_pass == COMPILE)
    {
      if ((curfn = popLabel(L_FUNC)) == (struct Pc *)0 ||
          (curfn->token + 1)->u.identifier->defaultType != V_VOID)
        {
          if (curfn != (struct Pc *)0)
            {
              pushLabel(L_FUNC, curfn);
            }

          return Value_new_ERROR(value, STRAYSUBEND, topLabelDescription());
        }
    }

  ++g_pc.token;
  if (g_pass == INTERPRET)
    {
      return Value_new_VOID(value);
    }
  else
    {
      if (g_pass == DECLARE)
        {
          Global_endfunction(&g_globals, (curfn->token + 1)->u.identifier, &g_pc);
        }

      Auto_funcEnd(&g_stack);
    }

  return (struct Value *)0;
}

struct Value *stmt_ENDSELECT(struct Value *value)
{
  struct Pc statementpc = g_pc;

  ++g_pc.token;
  if (g_pass == DECLARE || g_pass == COMPILE)
    {
      struct Pc *selectcasepc;

      if ((selectcasepc = popLabel(L_SELECTCASE)))
        {
          selectcasepc->token->u.selectcase->endselect = g_pc;
        }
      else
        {
          g_pc = statementpc;
          return Value_new_ERROR(value, STRAYENDSELECT);
        }
    }

  return (struct Value *)0;
}

struct Value *stmt_ENVIRON(struct Value *value)
{
  struct Pc epc = g_pc;

  ++g_pc.token;
  if (eval(value, _("environment variable"))->type == V_ERROR ||
      Value_retype(value, V_STRING)->type == V_ERROR)
    {
      return value;
    }

  if (g_pass == INTERPRET && value->u.string.character)
    {
      if (putenv(value->u.string.character) == -1)
        {
          Value_destroy(value);
          g_pc = epc;
          return Value_new_ERROR(value, ENVIRONFAILED, strerror(errno));
        }
    }

  Value_destroy(value);
  return (struct Value *)0;
}

struct Value *stmt_FNEXIT(struct Value *value)
{
  struct Pc *curfn = (struct Pc *)0;

  if (g_pass == DECLARE || g_pass == COMPILE)
    {
      if ((curfn = findLabel(L_FUNC)) == (struct Pc *)0 ||
          (curfn->token + 1)->u.identifier->defaultType == V_VOID)
        {
          return Value_new_ERROR(value, STRAYFNEXIT);
        }
    }

  ++g_pc.token;
  if (g_pass == INTERPRET)
    {
      return Value_clone(value,
                         Var_value(Auto_local(&g_stack, 0), 0, (int *)0,
                                   (struct Value *)0));
    }

  return (struct Value *)0;
}

struct Value *stmt_COLON_EOL(struct Value *value)
{
  return (struct Value *)0;
}

struct Value *stmt_QUOTE_REM(struct Value *value)
{
  ++g_pc.token;
  return (struct Value *)0;
}

struct Value *stmt_EQ_FNRETURN_FNEND(struct Value *value)
{
  struct Pc *curfn = (struct Pc *)0;
  struct Pc eqpc = g_pc;
  enum TokenType t = g_pc.token->type;

  if (g_pass == DECLARE || g_pass == COMPILE)
    {
      if (t == T_EQ)
        {
          if ((curfn = popLabel(L_FUNC)) == (struct Pc *)0)
            {
              return Value_new_ERROR(value, STRAYENDEQ);
            }

          if ((eqpc.token->u.type =
               (curfn->token + 1)->u.identifier->defaultType) == V_VOID)
            {
              return Value_new_ERROR(value, STRAYENDEQ);
            }
        }
      else if (t == T_FNEND)
        {
          if ((curfn = popLabel(L_FUNC)) == (struct Pc *)0)
            {
              return Value_new_ERROR(value, STRAYENDFN);
            }

          if ((eqpc.token->u.type =
               (curfn->token + 1)->u.identifier->defaultType) == V_VOID)
            {
              return Value_new_ERROR(value, STRAYENDFN);
            }
        }
      else
        {
          if ((curfn = findLabel(L_FUNC)) == (struct Pc *)0)
            {
              return Value_new_ERROR(value, STRAYFNRETURN);
            }

          if ((eqpc.token->u.type =
               (curfn->token + 1)->u.identifier->defaultType) == V_VOID)
            {
              return Value_new_ERROR(value, STRAYFNRETURN);
            }
        }
    }

  ++g_pc.token;
  if (eval(value, _("return"))->type == V_ERROR ||
      Value_retype(value, eqpc.token->u.type)->type == V_ERROR)
    {
      if (g_pass != INTERPRET)
        {
          Auto_funcEnd(&g_stack);
        }

      g_pc = eqpc;
      return value;
    }

  if (g_pass == INTERPRET)
    {
      return value;
    }
  else
    {
      Value_destroy(value);
      if (t == T_EQ || t == T_FNEND)
        {
          if (g_pass == DECLARE)
            {
              Global_endfunction(&g_globals, (curfn->token + 1)->u.identifier,
                                 &g_pc);
            }

          Auto_funcEnd(&g_stack);
        }
    }

  return (struct Value *)0;
}

struct Value *stmt_ERASE(struct Value *value)
{
  ++g_pc.token;
  while (1)
    {
      if (g_pc.token->type != T_IDENTIFIER)
        {
          return Value_new_ERROR(value, MISSINGARRIDENT);
        }

      if (g_pass == DECLARE &&
          Global_variable(&g_globals, g_pc.token->u.identifier,
                          g_pc.token->u.identifier->defaultType, GLOBALARRAY,
                          0) == 0)
        {
          return Value_new_ERROR(value, REDECLARATION);
        }

      if (g_pass == INTERPRET)
        {
          Var_destroy(&g_pc.token->u.identifier->sym->u.var);
        }

      ++g_pc.token;
      if (g_pc.token->type == T_COMMA)
        {
          ++g_pc.token;
        }
      else
        {
          break;
        }
    }

  return (struct Value *)0;
}

struct Value *stmt_EXITDO(struct Value *value)
{
  if (g_pass == INTERPRET)
    {
      g_pc = g_pc.token->u.exitdo;
    }
  else
    {
      if (g_pass == COMPILE)
        {
          struct Pc *exitdo;

          if ((exitdo = findLabel(L_DO)) == (struct Pc *)0 &&
              (exitdo = findLabel(L_DOcondition)) == (struct Pc *)0)
            {
              return Value_new_ERROR(value, STRAYEXITDO);
            }

          g_pc.token->u.exitdo = exitdo->token->u.exitdo;
        }

      ++g_pc.token;
    }

  return (struct Value *)0;
}

struct Value *stmt_EXITFOR(struct Value *value)
{
  if (g_pass == INTERPRET)
    {
      g_pc = g_pc.token->u.exitfor;
    }
  else
    {
      if (g_pass == COMPILE)
        {
          struct Pc *exitfor;

          if ((exitfor = findLabel(L_FOR)) == (struct Pc *)0)
            {
              return Value_new_ERROR(value, STRAYEXITFOR);
            }

          g_pc.token->u.exitfor = exitfor->token->u.exitfor;
        }

      ++g_pc.token;
    }

  return (struct Value *)0;
}

struct Value *stmt_FIELD(struct Value *value)
{
  long int chn, offset, recLength = -1;

  ++g_pc.token;
  if (g_pc.token->type == T_CHANNEL)
    {
      ++g_pc.token;
    }

  if (eval(value, _("channel"))->type == V_ERROR ||
      Value_retype(value, V_INTEGER)->type == V_ERROR)
    {
      return value;
    }

  chn = value->u.integer;
  Value_destroy(value);
  if (g_pass == INTERPRET && (recLength = FS_recLength(chn)) == -1)
    {
      return Value_new_ERROR(value, IOERROR, FS_errmsg);
    }

  if (g_pc.token->type != T_COMMA)
    {
      return Value_new_ERROR(value, MISSINGCOMMA);
    }

  ++g_pc.token;
  offset = 0;
  while (1)
    {
      struct Pc curpc;
      struct Value *l;
      long int width;

      curpc = g_pc;
      if (eval(value, _("field width"))->type == V_ERROR ||
          Value_retype(value, V_INTEGER)->type == V_ERROR)
        {
          return value;
        }

      width = value->u.integer;
      Value_destroy(value);
      if (g_pass == INTERPRET && offset + width > recLength)
        {
          g_pc = curpc;
          return Value_new_ERROR(value, OUTOFRANGE, _("field width"));
        }

      if (g_pc.token->type != T_AS)
        {
          return Value_new_ERROR(value, MISSINGAS);
        }

      ++g_pc.token;
      curpc = g_pc;
      if (g_pc.token->type != T_IDENTIFIER)
        {
          return Value_new_ERROR(value, MISSINGVARIDENT);
        }

      if (g_pass == DECLARE &&
          Global_variable(&g_globals, g_pc.token->u.identifier,
                          g_pc.token->u.identifier->defaultType,
                          (g_pc.token + 1)->type ==
                          T_OP ? GLOBALARRAY : GLOBALVAR, 0) == 0)
        {
          return Value_new_ERROR(value, REDECLARATION);
        }

      if ((l = lvalue(value))->type == V_ERROR)
        {
          return value;
        }

      if (g_pass != DECLARE && l->type != V_STRING)
        {
          g_pc = curpc;
          return Value_new_ERROR(value, TYPEMISMATCH4);
        }

      if (g_pass == INTERPRET)
        {
          FS_field(chn, &l->u.string, offset, width);
        }

      offset += width;
      if (g_pc.token->type == T_COMMA)
        {
          ++g_pc.token;
        }
      else
        {
          break;
        }
    }

  return (struct Value *)0;
}

struct Value *stmt_FOR(struct Value *value)
{
  struct Pc forpc = g_pc;
  struct Pc varpc;
  struct Pc limitpc;
  struct Value limit, stepValue;

  ++g_pc.token;
  varpc = g_pc;
  if (g_pc.token->type != T_IDENTIFIER)
    {
      return Value_new_ERROR(value, MISSINGLOOPIDENT);
    }

  if (assign(value)->type == V_ERROR)
    {
      return value;
    }

  if (g_pass == INTERPRET)
    {
      ++g_pc.token;
      if (eval(&limit, (const char *)0)->type == V_ERROR)
        {
          *value = limit;
          return value;
        }

      Value_retype(&limit, value->type);
      assert(limit.type != V_ERROR);
      if (g_pc.token->type == T_STEP)     /* STEP x */
        {
          struct Pc stepPc;

          ++g_pc.token;
          stepPc = g_pc;
          if (eval(&stepValue, "`step'")->type == V_ERROR)
            {
              Value_destroy(value);
              *value = stepValue;
              g_pc = stepPc;
              return value;
            }

          Value_retype(&stepValue, value->type);
          assert(stepValue.type != V_ERROR);
        }
      else                      /* implicit numeric STEP */
        {
          if (value->type == V_INTEGER)
            {
              VALUE_NEW_INTEGER(&stepValue, 1);
            }
          else
            {
              VALUE_NEW_REAL(&stepValue, 1.0);
            }
        }

      if (Value_exitFor(value, &limit, &stepValue))
        {
          g_pc = forpc.token->u.exitfor;
        }

      Value_destroy(&limit);
      Value_destroy(&stepValue);
      Value_destroy(value);
    }
  else
    {
      pushLabel(L_FOR, &forpc);
      pushLabel(L_FOR_VAR, &varpc);
      if (g_pc.token->type != T_TO)
        {
          Value_destroy(value);
          return Value_new_ERROR(value, MISSINGTO);
        }

      ++g_pc.token;
      pushLabel(L_FOR_LIMIT, &g_pc);
      limitpc = g_pc;
      if (eval(&limit, (const char *)0) == (struct Value *)0)
        {
          Value_destroy(value);
          return Value_new_ERROR(value, MISSINGEXPR, "`to'");
        }

      if (limit.type == V_ERROR)
        {
          Value_destroy(value);
          *value = limit;
          return value;
        }

      if (g_pass != DECLARE)
        {
          struct Symbol *sym = varpc.token->u.identifier->sym;

          if (VALUE_RETYPE
              (&limit, sym->type == GLOBALVAR ||
               sym->type == GLOBALARRAY ? sym->u.var.type : Auto_varType(&g_stack,
                                                                         sym))->type
              == V_ERROR)
            {
              Value_destroy(value);
              *value = limit;
              g_pc = limitpc;
              return value;
            }
        }

      Value_destroy(&limit);
      if (g_pc.token->type == T_STEP)     /* STEP x */
        {
          struct Pc stepPc;

          ++g_pc.token;
          stepPc = g_pc;
          if (eval(&stepValue, "`step'")->type == V_ERROR ||
              (g_pass != DECLARE &&
               Value_retype(&stepValue, value->type)->type == V_ERROR))
            {
              Value_destroy(value);
              *value = stepValue;
              g_pc = stepPc;
              return value;
            }
        }
      else                      /* implicit numeric STEP */
        {
          VALUE_NEW_INTEGER(&stepValue, 1);
          if (g_pass != DECLARE &&
              VALUE_RETYPE(&stepValue, value->type)->type == V_ERROR)
            {
              Value_destroy(value);
              *value = stepValue;
              Value_errorPrefix(value, _("implicit STEP 1:"));
              return value;
            }
        }

      pushLabel(L_FOR_BODY, &g_pc);
      Value_destroy(&stepValue);
      Value_destroy(value);
    }

  return (struct Value *)0;
}

struct Value *stmt_GET_PUT(struct Value *value)
{
  struct Pc statementpc = g_pc;
  int put = g_pc.token->type == T_PUT;
  long int chn;
  struct Pc errpc;

  ++g_pc.token;
  if (g_pc.token->type == T_CHANNEL)
    {
      ++g_pc.token;
    }

  if (eval(value, _("channel"))->type == V_ERROR ||
      Value_retype(value, V_INTEGER)->type == V_ERROR)
    {
      return value;
    }

  chn = value->u.integer;
  Value_destroy(value);
  if (g_pc.token->type == T_COMMA)
    {
      ++g_pc.token;
      errpc = g_pc;
      if (eval(value, (const char *)0)) /* process record number/position */
        {
          int rec;

          if (value->type == V_ERROR ||
              Value_retype(value, V_INTEGER)->type == V_ERROR)
            {
              return value;
            }

          rec = value->u.integer;
          Value_destroy(value);
          if (g_pass == INTERPRET)
            {
              if (rec < 1)
                {
                  g_pc = errpc;
                  return Value_new_ERROR(value, OUTOFRANGE, _("record number"));
                }

              if (FS_seek((int)chn, rec - 1) == -1)
                {
                  g_pc = statementpc;
                  return Value_new_ERROR(value, IOERROR, FS_errmsg);
                }
            }
        }

    }

  if (g_pc.token->type == T_COMMA)        /* BINARY mode get/put */
    {
      int res = -1;

      ++g_pc.token;
      if (put)
        {
          if (eval(value, _("`put'/`get' data"))->type == V_ERROR)
            {
              return value;
            }

          if (g_pass == INTERPRET)
            {
              switch (value->type)
                {
                case V_INTEGER:
                  res = FS_putbinaryInteger(chn, value->u.integer);
                  break;

                case V_REAL:
                  res = FS_putbinaryReal(chn, value->u.real);
                  break;

                case V_STRING:
                  res = FS_putbinaryString(chn, &value->u.string);
                  break;

                default:
                  assert(0);
                }
            }

          Value_destroy(value);
        }
      else
        {
          struct Value *l;

          if (g_pc.token->type != T_IDENTIFIER)
            {
              return Value_new_ERROR(value, MISSINGPROCIDENT);
            }

          if (g_pass == DECLARE)
            {
              if (((g_pc.token + 1)->type == T_OP ||
                   Auto_find(&g_stack, g_pc.token->u.identifier) == 0) &&
                  Global_variable(&g_globals, g_pc.token->u.identifier,
                                  g_pc.token->u.identifier->defaultType,
                                  (g_pc.token + 1)->type ==
                                  T_OP ? GLOBALARRAY : GLOBALVAR, 0) == 0)
                {
                  return Value_new_ERROR(value, REDECLARATION);
                }
            }

          if ((l = lvalue(value))->type == V_ERROR)
            {
              return value;
            }

          if (g_pass == INTERPRET)
            {
              switch (l->type)
                {
                case V_INTEGER:
                  res = FS_getbinaryInteger(chn, &l->u.integer);
                  break;

                case V_REAL:
                  res = FS_getbinaryReal(chn, &l->u.real);
                  break;

                case V_STRING:
                  res = FS_getbinaryString(chn, &l->u.string);
                  break;

                default:
                  assert(0);
                }
            }
        }

      if (g_pass == INTERPRET && res == -1)
        {
          g_pc = statementpc;
          return Value_new_ERROR(value, IOERROR, FS_errmsg);
        }
    }
  else if (g_pass == INTERPRET && ((put ? FS_put : FS_get) (chn)) == -1)
    {
      g_pc = statementpc;
      return Value_new_ERROR(value, IOERROR, FS_errmsg);
    }

  return (struct Value *)0;
}

struct Value *stmt_GOSUB(struct Value *value)
{
  if (g_pass == INTERPRET)
    {
      if (!g_program.runnable &&
          compileProgram(value, !DIRECTMODE)->type == V_ERROR)
        {
          return value;
        }

      g_pc.token += 2;
      Auto_pushGosubRet(&g_stack, &g_pc);
      g_pc = (g_pc.token - 2)->u.gosubpc;
      Program_trace(&g_program, &g_pc, 0, 1);
    }

  if (g_pass == DECLARE || g_pass == COMPILE)
    {
      struct Token *gosubpc = g_pc.token;

      ++g_pc.token;
      if (g_pc.token->type != T_INTEGER)
        {
          return Value_new_ERROR(value, MISSINGLINENUMBER);
        }

      if (Program_goLine(&g_program, g_pc.token->u.integer, &gosubpc->u.gosubpc) ==
          (struct Pc *)0)
        {
          return Value_new_ERROR(value, NOSUCHLINE);
        }

      if (g_pass == COMPILE &&
          Program_scopeCheck(&g_program, &gosubpc->u.gosubpc, findLabel(L_FUNC)))
        {
          return Value_new_ERROR(value, OUTOFSCOPE);
        }

      ++g_pc.token;
    }

  return (struct Value *)0;
}

struct Value *stmt_RESUME_GOTO(struct Value *value)
{
  if (g_pass == INTERPRET)
    {
      if (!g_program.runnable &&
          compileProgram(value, !DIRECTMODE)->type == V_ERROR)
        {
          return value;
        }

      if (g_pc.token->type == T_RESUME)
        {
          if (!g_stack.resumeable)
            {
              return Value_new_ERROR(value, STRAYRESUME);
            }

          g_stack.resumeable = 0;
        }

      g_pc = g_pc.token->u.gotopc;
      Program_trace(&g_program, &g_pc, 0, 1);
    }
  else if (g_pass == DECLARE || g_pass == COMPILE)
    {
      struct Token *gotopc = g_pc.token;

      ++g_pc.token;
      if (g_pc.token->type != T_INTEGER)
        {
          return Value_new_ERROR(value, MISSINGLINENUMBER);
        }

      if (Program_goLine(&g_program, g_pc.token->u.integer, &gotopc->u.gotopc) ==
          (struct Pc *)0)
        {
          return Value_new_ERROR(value, NOSUCHLINE);
        }

      if (g_pass == COMPILE &&
          Program_scopeCheck(&g_program, &gotopc->u.gotopc, findLabel(L_FUNC)))
        {
          return Value_new_ERROR(value, OUTOFSCOPE);
        }

      ++g_pc.token;
    }

  return (struct Value *)0;
}

struct Value *stmt_KILL(struct Value *value)
{
  struct Pc statementpc = g_pc;

  ++g_pc.token;
  if (eval(value, _("file name"))->type == V_ERROR ||
      (g_pass != DECLARE && Value_retype(value, V_STRING)->type == V_ERROR))
    {
      return value;
    }

  if (g_pass == INTERPRET && unlink(value->u.string.character) == -1)
    {
      const char *msg = strerror(errno);

      Value_destroy(value);
      g_pc = statementpc;
      return Value_new_ERROR(value, IOERROR, msg);
    }
  else
    {
      Value_destroy(value);
    }

  return (struct Value *)0;
}

struct Value *stmt_LET(struct Value *value)
{
  ++g_pc.token;
  if (g_pc.token->type != T_IDENTIFIER)
    {
      return Value_new_ERROR(value, MISSINGVARIDENT);
    }

  if (assign(value)->type == V_ERROR)
    {
      return value;
    }

  if (g_pass != INTERPRET)
    {
      Value_destroy(value);
    }

  return (struct Value *)0;
}

struct Value *stmt_LINEINPUT(struct Value *value)
{
  int channel = 0;
  struct Pc lpc;
  struct Value *l;

  ++g_pc.token;
  if (g_pc.token->type == T_CHANNEL)
    {
      ++g_pc.token;
      if (eval(value, _("channel"))->type == V_ERROR ||
          Value_retype(value, V_INTEGER)->type == V_ERROR)
        {
          return value;
        }

      channel = value->u.integer;
      Value_destroy(value);
      if (g_pc.token->type != T_COMMA)
        {
          return Value_new_ERROR(value, MISSINGCOMMA);
        }
      else
        {
          ++g_pc.token;
        }
    }

  /* prompt */

  if (g_pc.token->type == T_STRING)
    {
      if (g_pass == INTERPRET && channel == 0)
        {
          FS_putString(channel, g_pc.token->u.string);
        }

      ++g_pc.token;
      if (g_pc.token->type != T_SEMICOLON && g_pc.token->type != T_COMMA)
        {
          return Value_new_ERROR(value, MISSINGSEMICOMMA);
        }

      ++g_pc.token;
    }

  if (g_pass == INTERPRET && channel == 0)
    {
      FS_flush(channel);
    }

  if (g_pc.token->type != T_IDENTIFIER)
    {
      return Value_new_ERROR(value, MISSINGVARIDENT);
    }

  if (g_pass == DECLARE &&
      Global_variable(&g_globals, g_pc.token->u.identifier,
                      g_pc.token->u.identifier->defaultType,
                      (g_pc.token + 1)->type == T_OP ? GLOBALARRAY : GLOBALVAR,
                      0) == 0)
    {
      return Value_new_ERROR(value, REDECLARATION);
    }

  lpc = g_pc;
  if (((l = lvalue(value))->type) == V_ERROR)
    {
      return value;
    }

  if (g_pass == COMPILE && l->type != V_STRING)
    {
      g_pc = lpc;
      return Value_new_ERROR(value, TYPEMISMATCH4);
    }

  if (g_pass == INTERPRET)
    {
      String_size(&l->u.string, 0);
      if (FS_appendToString(channel, &l->u.string, 1) == -1)
        {
          return Value_new_ERROR(value, IOERROR, FS_errmsg);
        }

      if (l->u.string.length == 0)
        {
          return Value_new_ERROR(value, IOERROR, _("end of file"));
        }

      if (l->u.string.character[l->u.string.length - 1] == '\n')
        {
          String_size(&l->u.string, l->u.string.length - 1);
        }
    }

  return (struct Value *)0;
}

struct Value *stmt_LIST_LLIST(struct Value *value)
{
  struct Pc from, to;
  int f = 0, t = 0, channel;

  channel = (g_pc.token->type == T_LLIST ? LPCHANNEL : STDCHANNEL);
  ++g_pc.token;
  if (g_pc.token->type == T_INTEGER)
    {
      if (g_pass == INTERPRET &&
          Program_fromLine(&g_program, g_pc.token->u.integer,
                           &from) == (struct Pc *)0)
        {
          return Value_new_ERROR(value, NOSUCHLINE);
        }

      f = 1;
      ++g_pc.token;
    }
  else if (g_pc.token->type != T_MINUS && g_pc.token->type != T_COMMA)
    {
      if (eval(value, (const char *)0))
        {
          if (value->type == V_ERROR ||
              (g_pass != DECLARE &&
               Value_retype(value, V_INTEGER)->type == V_ERROR))
            {
              return value;
            }

          if (g_pass == INTERPRET &&
              Program_fromLine(&g_program, value->u.integer,
                               &from) == (struct Pc *)0)
            {
              return Value_new_ERROR(value, NOSUCHLINE);
            }

          f = 1;
          Value_destroy(value);
        }
    }

  if (g_pc.token->type == T_MINUS || g_pc.token->type == T_COMMA)
    {
      ++g_pc.token;
      if (eval(value, (const char *)0))
        {
          if (value->type == V_ERROR ||
              (g_pass != DECLARE &&
               Value_retype(value, V_INTEGER)->type == V_ERROR))
            {
              return value;
            }

          if (g_pass == INTERPRET &&
              Program_toLine(&g_program, value->u.integer, &to) == (struct Pc *)0)
            {
              return Value_new_ERROR(value, NOSUCHLINE);
            }

          t = 1;
          Value_destroy(value);
        }
    }
  else if (f == 1)
    {
      to = from;
      t  = 1;
    }

  if (g_pass == INTERPRET)
    {
      /* Some implementations do not require direct mode */

      if (Program_list
          (&g_program, channel, channel == STDCHANNEL, f ? &from : (struct Pc *)0,
           t ? &to : (struct Pc *)0, value))
        {
          return value;
        }
    }

  return (struct Value *)0;
}

struct Value *stmt_LOAD(struct Value *value)
{
  struct Pc loadpc;

  if (g_pass == INTERPRET && !DIRECTMODE)
    {
      return Value_new_ERROR(value, NOTINPROGRAMMODE);
    }

  ++g_pc.token;
  loadpc = g_pc;
  if (eval(value, _("file name"))->type == V_ERROR ||
      Value_retype(value, V_STRING)->type == V_ERROR)
    {
      g_pc = loadpc;
      return value;
    }

  if (g_pass == INTERPRET)
    {
      int dev;

      new();
      Program_setname(&g_program, value->u.string.character);
      if ((dev = FS_openin(value->u.string.character)) == -1)
        {
          g_pc = loadpc;
          Value_destroy(value);
          return Value_new_ERROR(value, IOERROR, FS_errmsg);
        }

      FS_width(dev, 0);
      Value_destroy(value);
      if (Program_merge(&g_program, dev, value))
        {
          g_pc = loadpc;
          return value;
        }

      FS_close(dev);
      g_program.unsaved = 0;
    }
  else
    {
      Value_destroy(value);
    }

  return (struct Value *)0;
}

struct Value *stmt_LOCAL(struct Value *value)
{
  struct Pc *curfn = (struct Pc *)0;

  if (g_pass == DECLARE || g_pass == COMPILE)
    {
      if ((curfn = findLabel(L_FUNC)) == (struct Pc *)0)
        return Value_new_ERROR(value, STRAYLOCAL);
    }

  ++g_pc.token;
  while (1)
    {
      if (g_pc.token->type != T_IDENTIFIER)
        {
          return Value_new_ERROR(value, MISSINGVARIDENT);
        }

      if (g_pass == DECLARE || g_pass == COMPILE)
        {
          struct Symbol *fnsym;

          if (Auto_variable(&g_stack, g_pc.token->u.identifier) == 0)
            return Value_new_ERROR(value, ALREADYLOCAL);
          if (g_pass == DECLARE)
            {
              assert(curfn->token->type == T_DEFFN ||
                     curfn->token->type == T_DEFPROC ||
                     curfn->token->type == T_SUB ||
                     curfn->token->type == T_FUNCTION);
              fnsym = (curfn->token + 1)->u.identifier->sym;
              assert(fnsym);
              fnsym->u.sub.u.def.localTypes =
                realloc(fnsym->u.sub.u.def.localTypes,
                        sizeof(enum ValueType) *
                        (fnsym->u.sub.u.def.localLength + 1));
              fnsym->u.sub.u.def.localTypes[fnsym->u.sub.u.def.localLength] =
                g_pc.token->u.identifier->defaultType;
              ++fnsym->u.sub.u.def.localLength;
            }
        }

      ++g_pc.token;
      if (g_pc.token->type == T_COMMA)
        {
          ++g_pc.token;
        }
      else
        {
          break;
        }
    }

  return (struct Value *)0;
}

struct Value *stmt_LOCATE(struct Value *value)
{
  long int line, column;
  struct Pc argpc;
  struct Pc statementpc = g_pc;

  ++g_pc.token;
  argpc = g_pc;
  if (eval(value, _("row"))->type == V_ERROR ||
      Value_retype(value, V_INTEGER)->type == V_ERROR)
    {
      return value;
    }

  line = value->u.integer;
  Value_destroy(value);
  if (g_pass == INTERPRET && line < 1)
    {
      g_pc = argpc;
      return Value_new_ERROR(value, OUTOFRANGE, _("row"));
    }

  if (g_pc.token->type == T_COMMA)
    {
      ++g_pc.token;
    }
  else
    {
      return Value_new_ERROR(value, MISSINGCOMMA);
    }

  argpc = g_pc;
  if (eval(value, _("column"))->type == V_ERROR ||
      Value_retype(value, V_INTEGER)->type == V_ERROR)
    {
      return value;
    }

  column = value->u.integer;
  Value_destroy(value);
  if (g_pass == INTERPRET && column < 1)
    {
      g_pc = argpc;
      return Value_new_ERROR(value, OUTOFRANGE, _("column"));
    }

  if (g_pass == INTERPRET && FS_locate(STDCHANNEL, line, column) == -1)
    {
      g_pc = statementpc;
      return Value_new_ERROR(value, IOERROR, FS_errmsg);
    }

  return (struct Value *)0;
}

struct Value *stmt_LOCK_UNLOCK(struct Value *value)
{
  int lock = g_pc.token->type == T_LOCK;
  int channel;

  ++g_pc.token;
  if (g_pc.token->type == T_CHANNEL)
    {
      ++g_pc.token;
    }

  if (eval(value, _("channel"))->type == V_ERROR ||
      Value_retype(value, V_INTEGER)->type == V_ERROR)
    {
      return value;
    }

  channel = value->u.integer;
  Value_destroy(value);
  if (g_pass == INTERPRET)
    {
      if (FS_lock(channel, 0, 0, lock ? FS_LOCK_EXCLUSIVE : FS_LOCK_NONE, 1) ==
          -1)
        {
          return Value_new_ERROR(value, IOERROR, FS_errmsg);
        }
    }

  return (struct Value *)0;
}

struct Value *stmt_LOOP(struct Value *value)
{
  struct Pc looppc = g_pc;
  struct Pc *dopc;

  ++g_pc.token;
  if (g_pass == INTERPRET)
    {
      g_pc = looppc.token->u.dopc;
    }

  if (g_pass == DECLARE || g_pass == COMPILE)
    {
      if ((dopc = popLabel(L_DO)) == (struct Pc *)0 &&
          (dopc = popLabel(L_DOcondition)) == (struct Pc *)0)
        {
          return Value_new_ERROR(value, STRAYLOOP);
        }

      looppc.token->u.dopc = *dopc;
      dopc->token->u.exitdo = g_pc;
    }

  return (struct Value *)0;
}

struct Value *stmt_LOOPUNTIL(struct Value *value)
{
  struct Pc loopuntilpc = g_pc;
  struct Pc *dopc;

  ++g_pc.token;
  if (eval(value, _("condition"))->type == V_ERROR)
    {
      return value;
    }

  if (g_pass == INTERPRET)
    {
      if (Value_isNull(value))
        g_pc = loopuntilpc.token->u.dopc;
      Value_destroy(value);
    }

  if (g_pass == DECLARE || g_pass == COMPILE)
    {
      if ((dopc = popLabel(L_DO)) == (struct Pc *)0)
        {
          return Value_new_ERROR(value, STRAYLOOPUNTIL);
        }

      loopuntilpc.token->u.until = *dopc;
      dopc->token->u.exitdo = g_pc;
    }

  return (struct Value *)0;
}

struct Value *stmt_LSET_RSET(struct Value *value)
{
  struct Value *l;
  struct Pc tmppc;
  int lset = (g_pc.token->type == T_LSET);

  ++g_pc.token;
  if (g_pass == DECLARE)
    {
      if (((g_pc.token + 1)->type == T_OP ||
           Auto_find(&g_stack, g_pc.token->u.identifier) == 0) &&
          Global_variable(&g_globals, g_pc.token->u.identifier,
                          g_pc.token->u.identifier->defaultType,
                          (g_pc.token + 1)->type ==
                          T_OP ? GLOBALARRAY : GLOBALVAR, 0) == 0)
        {
          return Value_new_ERROR(value, REDECLARATION);
        }
    }

  tmppc = g_pc;
  if ((l = lvalue(value))->type == V_ERROR)
    {
      return value;
    }

  if (g_pass == COMPILE && l->type != V_STRING)
    {
      g_pc = tmppc;
      return Value_new_ERROR(value, TYPEMISMATCH4);
    }

  if (g_pc.token->type != T_EQ)
    {
      return Value_new_ERROR(value, MISSINGEQ);
    }

  ++g_pc.token;
  tmppc = g_pc;
  if (eval(value, _("rhs"))->type == V_ERROR ||
      (g_pass != DECLARE && Value_retype(value, l->type)->type == V_ERROR))
    {
      g_pc = tmppc;
      return value;
    }

  if (g_pass == INTERPRET)
    {
      (lset ? String_lset : String_rset) (&l->u.string, &value->u.string);
    }

  Value_destroy(value);
  return (struct Value *)0;
}

struct Value *stmt_IDENTIFIER(struct Value *value)
{
  struct Pc here = g_pc;

  if (g_pass == DECLARE)
    {
      if (func(value)->type == V_ERROR)
        {
          return value;
        }
      else
        {
          Value_destroy(value);
        }

      if (g_pc.token->type == T_EQ || g_pc.token->type == T_COMMA)
        {
          g_pc = here;
          if (assign(value)->type == V_ERROR)
            {
              return value;
            }

          Value_destroy(value);
        }
    }
  else
    {
      if (g_pass == COMPILE)
        {
          if (((g_pc.token + 1)->type == T_OP ||
               Auto_find(&g_stack, g_pc.token->u.identifier) == 0) &&
              Global_find(&g_globals, g_pc.token->u.identifier,
                          (g_pc.token + 1)->type == T_OP) == 0)
            {
              return Value_new_ERROR(value, UNDECLARED);
            }
        }

      if (strcasecmp(g_pc.token->u.identifier->name, "mid$")
          && (g_pc.token->u.identifier->sym->type == USERFUNCTION ||
              g_pc.token->u.identifier->sym->type == BUILTINFUNCTION))
        {
          func(value);
          if (Value_retype(value, V_VOID)->type == V_ERROR)
            {
              return value;
            }

          Value_destroy(value);
        }
      else
        {
          if (assign(value)->type == V_ERROR)
            {
              return value;
            }

          if (g_pass != INTERPRET)
            {
              Value_destroy(value);
            }
        }
    }

  return (struct Value *)0;
}

struct Value *stmt_IF_ELSEIFIF(struct Value *value)
{
  struct Pc ifpc = g_pc;

  ++g_pc.token;
  if (eval(value, _("condition"))->type == V_ERROR)
    {
      return value;
    }

  if (g_pc.token->type != T_THEN)
    {
      Value_destroy(value);
      return Value_new_ERROR(value, MISSINGTHEN);
    }

  ++g_pc.token;
  if (g_pass == INTERPRET)
    {
      if (Value_isNull(value))
        {
          g_pc = ifpc.token->u.elsepc;
        }

      Value_destroy(value);
    }
  else
    {
      Value_destroy(value);
      if (g_pc.token->type == T_EOL)
        {
          pushLabel(L_IF, &ifpc);
        }
      else                      /* compile single line IF THEN ELSE recursively */
        {
          if (statements(value)->type == V_ERROR)
            {
              return value;
            }

          Value_destroy(value);
          if (g_pc.token->type == T_ELSE)
            {
              struct Pc elsepc = g_pc;

              ++g_pc.token;
              ifpc.token->u.elsepc = g_pc;
              if (ifpc.token->type == T_ELSEIFIF)
                {
                  (ifpc.token - 1)->u.elsepc = g_pc;
                }

              if (statements(value)->type == V_ERROR)
                {
                  return value;
                }

              Value_destroy(value);
              elsepc.token->u.endifpc = g_pc;
            }
          else
            {
              ifpc.token->u.elsepc = g_pc;
              if (ifpc.token->type == T_ELSEIFIF)
                {
                  (ifpc.token - 1)->u.elsepc = g_pc;
                }
            }
        }

    }

  return (struct Value *)0;
}

struct Value *stmt_IMAGE(struct Value *value)
{
  ++g_pc.token;
  if (g_pc.token->type != T_STRING)
    {
      return Value_new_ERROR(value, MISSINGFMT);
    }

  ++g_pc.token;
  return (struct Value *)0;
}

struct Value *stmt_INPUT(struct Value *value)
{
  int channel = STDCHANNEL;
  int nl = 1;
  int extraprompt = 1;
  struct Token *inputdata = (struct Token *)0, *t = (struct Token *)0;
  struct Pc lvaluepc;

  ++g_pc.token;
  if (g_pc.token->type == T_CHANNEL)
    {
      ++g_pc.token;
      if (eval(value, _("channel"))->type == V_ERROR ||
          Value_retype(value, V_INTEGER)->type == V_ERROR)
        {
          return value;
        }

      channel = value->u.integer;
      Value_destroy(value);
      if (g_pc.token->type != T_COMMA)
        {
          return Value_new_ERROR(value, MISSINGCOMMA);
        }
      else
        {
          ++g_pc.token;
        }
    }

  if (g_pc.token->type == T_SEMICOLON)
    {
      nl = 0;
      ++g_pc.token;
    }

  /* prompt */

  if (g_pc.token->type == T_STRING)
    {
      if (g_pass == INTERPRET && channel == STDCHANNEL)
        {
          FS_putString(STDCHANNEL, g_pc.token->u.string);
        }

      ++g_pc.token;
      if (g_pc.token->type == T_COMMA || g_pc.token->type == T_COLON)
        {
          ++g_pc.token;
          extraprompt = 0;
        }
      else if (g_pc.token->type == T_SEMICOLON)
        {
          ++g_pc.token;
        }
      else
        {
          extraprompt = 0;
        }
    }

  if (g_pass == INTERPRET && channel == STDCHANNEL && extraprompt)
    {
      FS_putChars(STDCHANNEL, "? ");
    }

retry:
  if (g_pass == INTERPRET)        /* read input line and tokenise it */
    {
      struct String s;

      if (channel == STDCHANNEL)
        {
          FS_flush(STDCHANNEL);
        }

      String_new(&s);
      if (FS_appendToString(channel, &s, nl) == -1)
        {
          return Value_new_ERROR(value, IOERROR, FS_errmsg);
        }

      if (s.length == 0)
        {
          return Value_new_ERROR(value, IOERROR, _("end of file"));
        }

      inputdata = t = Token_newData(s.character);
      String_destroy(&s);
    }

  while (1)
    {
      struct Value *l;

      if (g_pc.token->type != T_IDENTIFIER)
        {
          return Value_new_ERROR(value, MISSINGVARIDENT);
        }

      if (g_pass == DECLARE &&
          Global_variable(&g_globals, g_pc.token->u.identifier,
                          g_pc.token->u.identifier->defaultType,
                          (g_pc.token + 1)->type ==
                          T_OP ? GLOBALARRAY : GLOBALVAR, 0) == 0)
        {
          return Value_new_ERROR(value, REDECLARATION);
        }

      lvaluepc = g_pc;
      if (((l = lvalue(value))->type) == V_ERROR)
        {
          return value;
        }

      if (g_pass == INTERPRET)
        {
          if (t->type == T_COMMA || t->type == T_EOL)
            {
              enum ValueType ltype = l->type;

              Value_destroy(l);
              Value_new_null(l, ltype);
            }
          else if (convert(value, l, t))
            {
              g_pc = lvaluepc;
              if (channel == STDCHANNEL)
                {
                  struct String s;

                  String_new(&s);
                  Value_toString(value, &s, ' ', -1, 0, 0, 0, 0, -1, 0, 0);
                  String_appendChars(&s, " ?? ");
                  FS_putString(STDCHANNEL, &s);
                  String_destroy(&s);
                  Value_destroy(value);
                  Token_destroy(inputdata);
                  goto retry;
                }
              else
                {
                  Token_destroy(inputdata);
                  return value;
                }
            }
          else
            {
              ++t;
            }

          if (g_pc.token->type == T_COMMA)
            {
              if (t->type == T_COMMA)
                {
                  ++t;
                }
              else
                {
                  Token_destroy(inputdata);
                  if (channel == STDCHANNEL)
                    {
                      FS_putChars(STDCHANNEL, "?? ");
                      ++g_pc.token;
                      goto retry;
                    }
                  else
                    {
                      g_pc = lvaluepc;
                      return Value_new_ERROR(value, MISSINGINPUTDATA);
                    }
                }
            }
        }

      if (g_pc.token->type == T_COMMA)
        {
          ++g_pc.token;
        }
      else
        {
          break;
        }
    }

  if (g_pass == INTERPRET)
    {
      if (t->type != T_EOL)
        {
          FS_putChars(STDCHANNEL, _("Too much input data\n"));
        }

      Token_destroy(inputdata);
    }

  return (struct Value *)0;
}

struct Value *stmt_MAT(struct Value *value)
{
  struct Var *var1, *var2, *var3 = (struct Var *)0;
  struct Pc oppc;
  enum TokenType op = T_EOL;

  oppc.line = -1;
  oppc.token = (struct Token *)0;
  ++g_pc.token;
  if (g_pc.token->type != T_IDENTIFIER)
    {
      return Value_new_ERROR(value, MISSINGMATIDENT);
    }

  if (g_pass == DECLARE &&
      Global_variable(&g_globals, g_pc.token->u.identifier,
                      g_pc.token->u.identifier->defaultType, GLOBALARRAY, 0) == 0)
    {
      return Value_new_ERROR(value, REDECLARATION);
    }

  var1 = &g_pc.token->u.identifier->sym->u.var;
  ++g_pc.token;
  if (g_pc.token->type != T_EQ)
    {
      return Value_new_ERROR(value, MISSINGEQ);
    }

  ++g_pc.token;
  if (g_pc.token->type == T_IDENTIFIER)   /* a = b [ +|-|* c ] */
    {
      if (g_pass == COMPILE)
        {
          if (((g_pc.token + 1)->type == T_OP ||
               Auto_find(&g_stack, g_pc.token->u.identifier) == 0) &&
              Global_find(&g_globals, g_pc.token->u.identifier, 1) == 0)
            return Value_new_ERROR(value, UNDECLARED);
        }

      var2 = &g_pc.token->u.identifier->sym->u.var;
      if (g_pass == INTERPRET &&
          ((var2->dim != 1 && var2->dim != 2) || var2->base < 0 ||
           var2->base > 1))
        {
          return Value_new_ERROR(value, NOMATRIX, var2->dim, var2->base);
        }

      if (g_pass == COMPILE &&
          Value_commonType[var1->type][var2->type] == V_ERROR)
        {
          return Value_new_typeError(value, var2->type, var1->type);
        }

      ++g_pc.token;
      if (g_pc.token->type == T_PLUS || g_pc.token->type == T_MINUS ||
          g_pc.token->type == T_MULT)
        {
          oppc = g_pc;
          op = g_pc.token->type;
          ++g_pc.token;
          if (g_pc.token->type != T_IDENTIFIER)
            {
              return Value_new_ERROR(value, MISSINGARRIDENT);
            }

          if (g_pass == COMPILE)
            {
              if (((g_pc.token + 1)->type == T_OP ||
                   Auto_find(&g_stack, g_pc.token->u.identifier) == 0) &&
                  Global_find(&g_globals, g_pc.token->u.identifier, 1) == 0)
                {
                  return Value_new_ERROR(value, UNDECLARED);
                }
            }

          var3 = &g_pc.token->u.identifier->sym->u.var;
          if (g_pass == INTERPRET &&
              ((var3->dim != 1 && var3->dim != 2) || var3->base < 0 ||
               var3->base > 1))
            {
              return Value_new_ERROR(value, NOMATRIX, var3->dim, var3->base);
            }

          ++g_pc.token;
        }

      if (g_pass != DECLARE)
        {
          if (var3 == (struct Var *)0)
            {
              if (Var_mat_assign(var1, var2, value, g_pass == INTERPRET))
                {
                  assert(oppc.line != -1);
                  g_pc = oppc;
                  return value;
                }
            }
          else if (op == T_MULT)
            {
              if (Var_mat_mult(var1, var2, var3, value, g_pass == INTERPRET))
                {
                  assert(oppc.line != -1);
                  g_pc = oppc;
                  return value;
                }
            }
          else if (Var_mat_addsub
                   (var1, var2, var3, op == T_PLUS, value, g_pass == INTERPRET))
            {
              assert(oppc.line != -1);
              g_pc = oppc;
              return value;
            }
        }
    }
  else if (g_pc.token->type == T_OP)
    {
      if (var1->type == V_STRING)
        {
          return Value_new_ERROR(value, TYPEMISMATCH5);
        }

      ++g_pc.token;
      if (eval(value, _("factor"))->type == V_ERROR)
        {
          return value;
        }

      if (g_pass == COMPILE &&
          Value_commonType[var1->type][value->type] == V_ERROR)
        {
          return Value_new_typeError(value, var1->type, value->type);
        }

      if (g_pc.token->type != T_CP)
        {
          Value_destroy(value);
          return Value_new_ERROR(value, MISSINGCP);
        }

      ++g_pc.token;
      if (g_pc.token->type != T_MULT)
        {
          Value_destroy(value);
          return Value_new_ERROR(value, MISSINGMULT);
        }

      oppc = g_pc;
      ++g_pc.token;
      if (g_pass == COMPILE)
        {
          if (((g_pc.token + 1)->type == T_OP ||
               Auto_find(&g_stack, g_pc.token->u.identifier) == 0) &&
              Global_find(&g_globals, g_pc.token->u.identifier, 1) == 0)
            {
              Value_destroy(value);
              return Value_new_ERROR(value, UNDECLARED);
            }
        }

      var2 = &g_pc.token->u.identifier->sym->u.var;
      if (g_pass == INTERPRET &&
          ((var2->dim != 1 && var2->dim != 2) || var2->base < 0 ||
           var2->base > 1))
        {
          Value_destroy(value);
          return Value_new_ERROR(value, NOMATRIX, var2->dim, var2->base);
        }

      if (g_pass != DECLARE &&
          Var_mat_scalarMult(var1, value, var2, g_pass == INTERPRET))
        {
          assert(oppc.line != -1);
          g_pc = oppc;
          return value;
        }

      Value_destroy(value);
      ++g_pc.token;
    }

  else if (g_pc.token->type == T_CON || g_pc.token->type == T_ZER ||
           g_pc.token->type == T_IDN)
    {
      op = g_pc.token->type;
      if (g_pass == COMPILE && Value_commonType[var1->type][V_INTEGER] == V_ERROR)
        {
          return Value_new_typeError(value, V_INTEGER, var1->type);
        }

      ++g_pc.token;
      if (g_pc.token->type == T_OP)
        {
          unsigned int dim, geometry[2];
          enum ValueType vartype = var1->type;

          ++g_pc.token;
          if (evalGeometry(value, &dim, geometry))
            {
              return value;
            }

          if (g_pass == INTERPRET)
            {
              Var_destroy(var1);
              Var_new(var1, vartype, dim, geometry, g_optionbase);
            }
        }

      if (g_pass == INTERPRET)
        {
          unsigned int i;
          int unused = 1 - var1->base;

          if ((var1->dim != 1 && var1->dim != 2) || var1->base < 0 ||
              var1->base > 1)
            {
              return Value_new_ERROR(value, NOMATRIX, var1->dim, var1->base);
            }

          if (var1->dim == 1)
            {
              for (i = unused; i < var1->geometry[0]; ++i)
                {
                  int c = -1;

                  Value_destroy(&(var1->value[i]));
                  switch (op)
                    {
                    case T_CON:
                      c = 1;
                      break;

                    case T_ZER:
                      c = 0;
                      break;

                    case T_IDN:
                      c = (i == unused ? 1 : 0);
                      break;

                    default:
                      assert(0);
                    }

                  if (var1->type == V_INTEGER)
                    {
                      Value_new_INTEGER(&(var1->value[i]), c);
                    }
                  else
                    {
                      Value_new_REAL(&(var1->value[i]), (double)c);
                    }
                }
            }
          else
            {
              int j;

              for (i = unused; i < var1->geometry[0]; ++i)
                {
                  for (j = unused; j < var1->geometry[1]; ++j)
                    {
                      int c = -1;

                      Value_destroy(&(var1->value[i * var1->geometry[1] + j]));
                      switch (op)
                        {
                        case T_CON:
                          c = 1;
                          break;

                        case T_ZER:
                          c = 0;
                          break;

                        case T_IDN:
                          c = (i == j ? 1 : 0);
                          break;

                        default:
                          assert(0);
                        }

                      if (var1->type == V_INTEGER)
                        {
                          Value_new_INTEGER(&
                                            (var1->value
                                             [i * var1->geometry[1] + j]), c);
                        }
                      else
                        {
                          Value_new_REAL(&
                                         (var1->
                                          value[i * var1->geometry[1] + j]),
                                         (double)c);
                        }
                    }
                }
            }
        }
    }

  else if (g_pc.token->type == T_TRN || g_pc.token->type == T_INV)
    {
      op = g_pc.token->type;
      ++g_pc.token;
      if (g_pc.token->type != T_OP)
        {
          return Value_new_ERROR(value, MISSINGOP);
        }

      ++g_pc.token;
      if (g_pc.token->type != T_IDENTIFIER)
        {
          return Value_new_ERROR(value, MISSINGMATIDENT);
        }

      if (g_pass == COMPILE)
        {
          if (((g_pc.token + 1)->type == T_OP ||
               Auto_find(&g_stack, g_pc.token->u.identifier) == 0) &&
              Global_find(&g_globals, g_pc.token->u.identifier, 1) == 0)
            {
              return Value_new_ERROR(value, UNDECLARED);
            }
        }

      var2 = &g_pc.token->u.identifier->sym->u.var;
      if (g_pass == COMPILE &&
          Value_commonType[var1->type][var2->type] == V_ERROR)
        {
          return Value_new_typeError(value, var2->type, var1->type);
        }

      if (g_pass == INTERPRET)
        {
          if (var2->dim != 2 || var2->base < 0 || var2->base > 1)
            {
              return Value_new_ERROR(value, NOMATRIX, var2->dim, var2->base);
            }

          switch (op)
            {
            case T_TRN:
              Var_mat_transpose(var1, var2);
              break;

            case T_INV:
              if (Var_mat_invert(var1, var2, &g_stack.lastdet, value))
                {
                  return value;
                }

              break;

            default:
              assert(0);
            }
        }

      ++g_pc.token;
      if (g_pc.token->type != T_CP)
        {
          return Value_new_ERROR(value, MISSINGCP);
        }

      ++g_pc.token;
    }
  else
    {
      return Value_new_ERROR(value, MISSINGEXPR, _("matrix"));
    }

  return (struct Value *)0;
}

struct Value *stmt_MATINPUT(struct Value *value)
{
  int channel = STDCHANNEL;

  ++g_pc.token;
  if (g_pc.token->type == T_CHANNEL)
    {
      ++g_pc.token;
      if (eval(value, _("channel"))->type == V_ERROR ||
          Value_retype(value, V_INTEGER)->type == V_ERROR)
        {
          return value;
        }

      channel = value->u.integer;
      Value_destroy(value);
      if (g_pc.token->type != T_COMMA)
        {
          return Value_new_ERROR(value, MISSINGCOMMA);
        }
      else
        {
          ++g_pc.token;
        }
    }

  while (1)
    {
      struct Pc lvaluepc;
      struct Var *var;

      lvaluepc = g_pc;
      if (g_pc.token->type != T_IDENTIFIER)
        {
          return Value_new_ERROR(value, MISSINGMATIDENT);
        }

      if (g_pass == DECLARE &&
          Global_variable(&g_globals, g_pc.token->u.identifier,
                          g_pc.token->u.identifier->defaultType, GLOBALARRAY,
                          0) == 0)
        {
          return Value_new_ERROR(value, REDECLARATION);
        }

      var = &g_pc.token->u.identifier->sym->u.var;
      ++g_pc.token;
      if (g_pc.token->type == T_OP)
        {
          unsigned int dim, geometry[2];
          enum ValueType vartype = var->type;

          ++g_pc.token;
          if (evalGeometry(value, &dim, geometry))
            {
              return value;
            }

          if (g_pass == INTERPRET)
            {
              Var_destroy(var);
              Var_new(var, vartype, dim, geometry, g_optionbase);
            }
        }

      if (g_pass == INTERPRET)
        {
          unsigned int i, j;
          int unused = 1 - var->base;
          int columns;
          struct Token *inputdata, *t;

          if (var->dim != 1 && var->dim != 2)
            {
              return Value_new_ERROR(value, NOMATRIX, var->dim);
            }

          columns = var->dim == 1 ? 0 : var->geometry[1];
          inputdata = t = (struct Token *)0;
          for (i = unused, j = unused; i < var->geometry[0];)
            {
              struct String s;

              if (!inputdata)
                {
                  if (channel == STDCHANNEL)
                    {
                      FS_putChars(STDCHANNEL, "? ");
                      FS_flush(STDCHANNEL);
                    }

                  String_new(&s);
                  if (FS_appendToString(channel, &s, 1) == -1)
                    {
                      return Value_new_ERROR(value, IOERROR, FS_errmsg);
                    }

                  if (s.length == 0)
                    {
                      return Value_new_ERROR(value, IOERROR, _("end of file"));
                    }

                  inputdata = t = Token_newData(s.character);
                  String_destroy(&s);
                }

              if (t->type == T_COMMA)
                {
                  Value_destroy(&(var->value[j * columns + i]));
                  Value_new_null(&(var->value[j * columns + i]), var->type);
                  ++t;
                }
              else if (t->type == T_EOL)
                {
                  while (i < var->geometry[0])
                    {
                      Value_destroy(&(var->value[j * columns + i]));
                      Value_new_null(&(var->value[j * columns + i]), var->type);
                      ++i;
                    }
                }
              else if (convert(value, &(var->value[j * columns + i]), t))
                {
                  Token_destroy(inputdata);
                  g_pc = lvaluepc;
                  return value;
                }
              else
                {
                  ++t;
                  ++i;
                  if (t->type == T_COMMA)
                    {
                      ++t;
                    }
                }

              if (i == var->geometry[0] && j < (columns - 1))
                {
                  i = unused;
                  ++j;
                  if (t->type == T_EOL)
                    {
                      Token_destroy(inputdata);
                      inputdata = (struct Token *)0;
                    }
                }
            }
        }

      if (g_pc.token->type == T_COMMA)
        {
          ++g_pc.token;
        }
      else
        {
          break;
        }
    }

  return (struct Value *)0;
}

struct Value *stmt_MATPRINT(struct Value *value)
{
  int chn = STDCHANNEL;
  int printusing = 0;
  struct Value usingval;
  struct String *using = (struct String *)0;
  size_t usingpos = 0;
  int notfirst = 0;

  ++g_pc.token;
  if (chn == STDCHANNEL && g_pc.token->type == T_CHANNEL)
    {
      ++g_pc.token;
      if (eval(value, _("channel"))->type == V_ERROR ||
          Value_retype(value, V_INTEGER)->type == V_ERROR)
        {
          return value;
        }

      chn = value->u.integer;
      Value_destroy(value);
      if (g_pc.token->type == T_COMMA)
        {
          ++g_pc.token;
        }
    }

  if (g_pc.token->type == T_USING)
    {
      struct Pc usingpc;

      usingpc = g_pc;
      printusing = 1;
      ++g_pc.token;
      if (g_pc.token->type == T_INTEGER)
        {
          if (g_pass == COMPILE &&
              Program_imageLine(&g_program, g_pc.token->u.integer,
                                &usingpc.token->u.image) == (struct Pc *)0)
            {
              return Value_new_ERROR(value, NOSUCHIMAGELINE);
            }
          else if (g_pass == INTERPRET)
            {
              using = usingpc.token->u.image.token->u.string;
            }

          Value_new_STRING(&usingval);
          ++g_pc.token;
        }
      else
        {
          if (eval(&usingval, _("format string"))->type == V_ERROR ||
              Value_retype(&usingval, V_STRING)->type == V_ERROR)
            {
              *value = usingval;
              return value;
            }

          using = &usingval.u.string;
        }

      if (g_pc.token->type != T_SEMICOLON)
        {
          Value_destroy(&usingval);
          return Value_new_ERROR(value, MISSINGSEMICOLON);
        }

      ++g_pc.token;
    }
  else
    {
      Value_new_STRING(&usingval);
      using = &usingval.u.string;
    }
  while (1)
    {
      struct Var *var;
      int zoned = 1;

      if (g_pc.token->type != T_IDENTIFIER)
        {
          if (notfirst)
            {
              break;
            }

          Value_destroy(&usingval);
          return Value_new_ERROR(value, MISSINGMATIDENT);
        }

      if (g_pass == DECLARE &&
          Global_variable(&g_globals, g_pc.token->u.identifier,
                          g_pc.token->u.identifier->defaultType, GLOBALARRAY,
                          0) == 0)
        {
          Value_destroy(&usingval);
          return Value_new_ERROR(value, REDECLARATION);
        }

      var = &g_pc.token->u.identifier->sym->u.var;
      ++g_pc.token;
      if (g_pc.token->type == T_SEMICOLON)
        {
          zoned = 0;
        }

      if (g_pass == INTERPRET)
        {
          unsigned int i, j;
          int unused = 1 - var->base;
          int g0, g1;

          if ((var->dim != 1 && var->dim != 2) || var->base < 0 ||
              var->base > 1)
            {
              return Value_new_ERROR(value, NOMATRIX, var->dim, var->base);
            }

          if ((notfirst ? FS_putChar(chn, '\n') : FS_nextline(chn)) == -1)
            {
              Value_destroy(&usingval);
              return Value_new_ERROR(value, IOERROR, FS_errmsg);
            }

          g0 = var->geometry[0];
          g1 = var->dim == 1 ? unused + 1 : var->geometry[1];
          for (i = unused; i < g0; ++i)
            {
              for (j = unused; j < g1; ++j)
                {
                  struct String s;

                  String_new(&s);
                  Value_clone(value,
                              &(var->value[var->dim == 1 ? i : i * g1 + j]));
                  if (Value_toStringUsing(value, &s, using, &usingpos)->type ==
                      V_ERROR)
                    {
                      Value_destroy(&usingval);
                      String_destroy(&s);
                      return value;
                    }

                  Value_destroy(value);
                  if (FS_putString(chn, &s) == -1)
                    {
                      Value_destroy(&usingval);
                      String_destroy(&s);
                      return Value_new_ERROR(value, IOERROR, FS_errmsg);
                    }

                  String_destroy(&s);
                  if (!printusing && zoned)
                    {
                      FS_nextcol(chn);
                    }
                }

              if (FS_putChar(chn, '\n') == -1)
                {
                  return Value_new_ERROR(value, IOERROR, FS_errmsg);
                }
            }
        }

      if (g_pc.token->type == T_COMMA || g_pc.token->type == T_SEMICOLON)
        {
          ++g_pc.token;
        }
      else
        {
          break;
        }

      notfirst = 1;
    }

  Value_destroy(&usingval);
  if (g_pass == INTERPRET)
    {
      if (FS_flush(chn) == -1)
        {
          return Value_new_ERROR(value, IOERROR, FS_errmsg);
        }
    }

  return (struct Value *)0;
}

struct Value *stmt_MATREAD(struct Value *value)
{
  ++g_pc.token;
  while (1)
    {
      struct Pc lvaluepc;
      struct Var *var;

      lvaluepc = g_pc;
      if (g_pc.token->type != T_IDENTIFIER)
        {
          return Value_new_ERROR(value, MISSINGMATIDENT);
        }

      if (g_pass == DECLARE &&
          Global_variable(&g_globals, g_pc.token->u.identifier,
                          g_pc.token->u.identifier->defaultType, GLOBALARRAY,
                          0) == 0)
        {
          return Value_new_ERROR(value, REDECLARATION);
        }

      var = &g_pc.token->u.identifier->sym->u.var;
      ++g_pc.token;
      if (g_pc.token->type == T_OP)
        {
          unsigned int dim, geometry[2];
          enum ValueType vartype = var->type;

          ++g_pc.token;
          if (evalGeometry(value, &dim, geometry))
            {
              return value;
            }

          if (g_pass == INTERPRET)
            {
              Var_destroy(var);
              Var_new(var, vartype, dim, geometry, g_optionbase);
            }
        }

      if (g_pass == INTERPRET)
        {
          unsigned int i;
          int unused = 1 - var->base;

          if ((var->dim != 1 && var->dim != 2) || var->base < 0 ||
              var->base > 1)
            {
              return Value_new_ERROR(value, NOMATRIX, var->dim, var->base);
            }

          if (var->dim == 1)
            {
              for (i = unused; i < var->geometry[0]; ++i)
                {
                  if (dataread(value, &(var->value[i])))
                    {
                      g_pc = lvaluepc;
                      return value;
                    }
                }
            }
          else
            {
              int j;

              for (i = unused; i < var->geometry[0]; ++i)
                {
                  for (j = unused; j < var->geometry[1]; ++j)
                    {
                      if (dataread
                          (value, &(var->value[i * var->geometry[1] + j])))
                        {
                          g_pc = lvaluepc;
                          return value;
                        }
                    }
                }
            }
        }

      if (g_pc.token->type == T_COMMA)
        {
          ++g_pc.token;
        }
      else
        {
          break;
        }
    }

  return (struct Value *)0;
}

struct Value *stmt_MATREDIM(struct Value *value)
{
  ++g_pc.token;
  while (1)
    {
      struct Var *var;
      unsigned int dim, geometry[2];

      if (g_pc.token->type != T_IDENTIFIER)
        {
          return Value_new_ERROR(value, MISSINGMATIDENT);
        }

      if (g_pass == DECLARE &&
          Global_variable(&g_globals, g_pc.token->u.identifier,
                          g_pc.token->u.identifier->defaultType, GLOBALARRAY,
                          0) == 0)
        {
          return Value_new_ERROR(value, REDECLARATION);
        }

      var = &g_pc.token->u.identifier->sym->u.var;
      ++g_pc.token;
      if (g_pc.token->type != T_OP)
        {
          return Value_new_ERROR(value, MISSINGOP);
        }

      ++g_pc.token;
      if (evalGeometry(value, &dim, geometry))
        {
          return value;
        }

      if (g_pass == INTERPRET &&
          Var_mat_redim(var, dim, geometry, value) != (struct Value *)0)
        {
          return value;
        }

      if (g_pc.token->type == T_COMMA)
        {
          ++g_pc.token;
        }
      else
        {
          break;
        }
    }

  return (struct Value *)0;
}

struct Value *stmt_MATWRITE(struct Value *value)
{
  int chn = STDCHANNEL;
  int notfirst = 0;
  int comma = 0;

  ++g_pc.token;
  if (g_pc.token->type == T_CHANNEL)
    {
      ++g_pc.token;
      if (eval(value, _("channel"))->type == V_ERROR ||
          Value_retype(value, V_INTEGER)->type == V_ERROR)
        {
          return value;
        }

      chn = value->u.integer;
      Value_destroy(value);
      if (g_pc.token->type == T_COMMA)
        {
          ++g_pc.token;
        }
    }

  while (1)
    {
      struct Var *var;

      if (g_pc.token->type != T_IDENTIFIER)
        {
          if (notfirst)
            {
              break;
            }

          return Value_new_ERROR(value, MISSINGMATIDENT);
        }

      notfirst = 1;
      if (g_pass == DECLARE &&
          Global_variable(&g_globals, g_pc.token->u.identifier,
                          g_pc.token->u.identifier->defaultType, GLOBALARRAY,
                          0) == 0)
        {
          return Value_new_ERROR(value, REDECLARATION);
        }

      var = &g_pc.token->u.identifier->sym->u.var;
      ++g_pc.token;
      if (g_pass == INTERPRET)
        {
          unsigned int i, j;
          int unused = 1 - var->base;
          int g0, g1;

          if ((var->dim != 1 && var->dim != 2) || var->base < 0 ||
              var->base > 1)
            {
              return Value_new_ERROR(value, NOMATRIX, var->dim, var->base);
            }

          g0 = var->geometry[0];
          g1 = var->dim == 1 ? unused + 1 : var->geometry[1];
          for (i = unused; i < g0; ++i)
            {
              comma = 0;
              for (j = unused; j < g1; ++j)
                {
                  struct String s;

                  String_new(&s);
                  Value_clone(value,
                              &(var->value[var->dim == 1 ? i : i * g1 + j]));
                  if (comma)
                    {
                      String_appendChar(&s, ',');
                    }

                  if (FS_putString(chn, Value_toWrite(value, &s)) == -1)
                    {
                      Value_destroy(value);
                      return Value_new_ERROR(value, IOERROR, FS_errmsg);
                    }

                  if (FS_flush(chn) == -1)
                    {
                      return Value_new_ERROR(value, IOERROR, FS_errmsg);
                    }

                  String_destroy(&s);
                  comma = 1;
                }

              FS_putChar(chn, '\n');
            }
        }

      if (g_pc.token->type == T_COMMA || g_pc.token->type == T_SEMICOLON)
        {
          ++g_pc.token;
        }
      else
        {
          break;
        }
    }

  if (g_pass == INTERPRET)
    {
      if (FS_flush(chn) == -1)
        {
          return Value_new_ERROR(value, IOERROR, FS_errmsg);
        }
    }

  return (struct Value *)0;
}

struct Value *stmt_NAME(struct Value *value)
{
  struct Pc namepc = g_pc;
  struct Value old;
  int res = -1, reserrno = -1;

  ++g_pc.token;
  if (eval(value, _("file name"))->type == V_ERROR ||
      Value_retype(value, V_STRING)->type == V_ERROR)
    {
      return value;
    }

  if (g_pc.token->type != T_AS)
    {
      Value_destroy(value);
      return Value_new_ERROR(value, MISSINGAS);
    }

  old = *value;
  ++g_pc.token;
  if (eval(value, _("file name"))->type == V_ERROR ||
      Value_retype(value, V_STRING)->type == V_ERROR)
    {
      Value_destroy(&old);
      return value;
    }

  if (g_pass == INTERPRET)
    {
      res = rename(old.u.string.character, value->u.string.character);
      reserrno = errno;
    }

  Value_destroy(&old);
  Value_destroy(value);
  if (g_pass == INTERPRET && res == -1)
    {
      g_pc = namepc;
      return Value_new_ERROR(value, IOERROR, strerror(reserrno));
    }

  return (struct Value *)0;
}

struct Value *stmt_NEW(struct Value *value)
{
  if (g_pass == INTERPRET)
    {
      if (!DIRECTMODE)
        {
          return Value_new_ERROR(value, NOTINPROGRAMMODE);
        }

      new();
    }

  ++g_pc.token;
  return (struct Value *)0;
}

struct Value *stmt_NEXT(struct Value *value)
{
  struct Next **next = &g_pc.token->u.next;
  int level = 0;

  if (g_pass == INTERPRET)
    {
      struct Value *l, inc;
      struct Pc savepc;

      ++g_pc.token;
      while (1)
        {
          /* get variable lvalue */

          savepc = g_pc;
          g_pc = (*next)[level].var;
          if ((l = lvalue(value))->type == V_ERROR)
            {
              return value;
            }

          g_pc = savepc;

          /* get limit value and increment */

          savepc = g_pc;
          g_pc = (*next)[level].limit;
          if (eval(value, _("limit"))->type == V_ERROR)
            {
              return value;
            }

          Value_retype(value, l->type);
          assert(value->type != V_ERROR);
          if (g_pc.token->type == T_STEP)
            {
              ++g_pc.token;
              if (eval(&inc, _("step"))->type == V_ERROR)
                {
                  Value_destroy(value);
                  *value = inc;
                  return value;
                }
            }
          else
            {
              VALUE_NEW_INTEGER(&inc, 1);
            }

          VALUE_RETYPE(&inc, l->type);
          assert(inc.type != V_ERROR);
          g_pc = savepc;

          Value_add(l, &inc, 1);
          if (Value_exitFor(l, value, &inc))
            {
              Value_destroy(value);
              Value_destroy(&inc);
              if (g_pc.token->type == T_IDENTIFIER)
                {
                  if (lvalue(value)->type == V_ERROR)
                    {
                      return value;
                    }

                  if (g_pc.token->type == T_COMMA)
                    {
                      ++g_pc.token;
                      ++level;
                    }
                  else
                    {
                      break;
                    }
                }
              else
                {
                  break;
                }
            }
          else
            {
              g_pc = (*next)[level].body;
              Value_destroy(value);
              Value_destroy(&inc);
              break;
            }
        }
    }
  else
    {
      struct Pc *body;

      ++g_pc.token;
      while (1)
        {
          if ((body = popLabel(L_FOR_BODY)) == (struct Pc *)0)
            {
              return Value_new_ERROR(value, STRAYNEXT, topLabelDescription());
            }

          if (level)
            {
              struct Next *more;

              more = realloc(*next, sizeof(struct Next) * (level + 1));
              *next = more;
            }

          (*next)[level].body = *body;
          (*next)[level].limit = *popLabel(L_FOR_LIMIT);
          (*next)[level].var = *popLabel(L_FOR_VAR);
          (*next)[level].fr = *popLabel(L_FOR);
          if (g_pc.token->type == T_IDENTIFIER)
            {
              if (cistrcmp
                  (g_pc.token->u.identifier->name,
                   (*next)[level].var.token->u.identifier->name))
                {
                  return Value_new_ERROR(value, FORMISMATCH);
                }

              if (g_pass == DECLARE &&
                  Global_variable(&g_globals, g_pc.token->u.identifier,
                                  g_pc.token->u.identifier->defaultType,
                                  (g_pc.token + 1)->type ==
                                  T_OP ? GLOBALARRAY : GLOBALVAR, 0) == 0)
                {
                  return Value_new_ERROR(value, REDECLARATION);
                }

              if (lvalue(value)->type == V_ERROR)
                {
                  return value;
                }

              if (g_pc.token->type == T_COMMA)
                {
                  ++g_pc.token;
                  ++level;
                }
              else
                {
                  break;
                }
            }
          else
            {
              break;
            }
        }

      while (level >= 0)
        {
          (*next)[level--].fr.token->u.exitfor = g_pc;
        }
    }

  return (struct Value *)0;
}

struct Value *stmt_ON(struct Value *value)
{
  struct On *on = &g_pc.token->u.on;

  ++g_pc.token;
  if (eval(value, _("selector"))->type == V_ERROR)
    {
      return value;
    }

  if (Value_retype(value, V_INTEGER)->type == V_ERROR)
    {
      return value;
    }

  if (g_pass == INTERPRET)
    {
      struct Pc newpc;

      if (value->u.integer > 0 && value->u.integer < on->pcLength)
        {
          newpc = on->pc[value->u.integer];
        }
      else
        {
          newpc = on->pc[0];
        }

      if (g_pc.token->type == T_GOTO)
        {
          g_pc = newpc;
        }
      else
        {
          g_pc = on->pc[0];
          Auto_pushGosubRet(&g_stack, &g_pc);
          g_pc = newpc;
        }

      Program_trace(&g_program, &g_pc, 0, 1);
    }
  else if (g_pass == DECLARE || g_pass == COMPILE)
    {
      Value_destroy(value);
      if (g_pc.token->type != T_GOTO && g_pc.token->type != T_GOSUB)
        {
          return Value_new_ERROR(value, MISSINGGOTOSUB);
        }

      ++g_pc.token;
      on->pcLength = 1;
      while (1)
        {
          on->pc = realloc(on->pc, sizeof(struct Pc) * ++on->pcLength);
          if (g_pc.token->type != T_INTEGER)
            {
              return Value_new_ERROR(value, MISSINGLINENUMBER);
            }

          if (Program_goLine
              (&g_program, g_pc.token->u.integer,
               &on->pc[on->pcLength - 1]) == (struct Pc *)0)
            {
              return Value_new_ERROR(value, NOSUCHLINE);
            }

          if (g_pass == COMPILE &&
              Program_scopeCheck(&g_program, &on->pc[on->pcLength - 1],
                                 findLabel(L_FUNC)))
            {
              return Value_new_ERROR(value, OUTOFSCOPE);
            }

          ++g_pc.token;
          if (g_pc.token->type == T_COMMA)
            {
              ++g_pc.token;
            }
          else
            {
              break;
            }
        }

      on->pc[0] = g_pc;
    }

  return (struct Value *)0;
}

struct Value *stmt_ONERROR(struct Value *value)
{
  if (DIRECTMODE)
    {
      return Value_new_ERROR(value, NOTINDIRECTMODE);
    }

  ++g_pc.token;
  if (g_pass == INTERPRET)
    {
      g_stack.onerror = g_pc;
      Program_nextLine(&g_program, &g_pc);
      return (struct Value *)0;
    }
  else
    {
      return &more_statements;
    }
}

struct Value *stmt_ONERRORGOTO0(struct Value *value)
{
  if (DIRECTMODE)
    {
      return Value_new_ERROR(value, NOTINDIRECTMODE);
    }

  if (g_pass == INTERPRET)
    {
      g_stack.onerror.line = -1;
      if (g_stack.resumeable)
        {
          g_pc = g_stack.erpc;
          return Value_clone(value, &g_stack.err);
        }
    }

  ++g_pc.token;
  return (struct Value *)0;
}

struct Value *stmt_ONERROROFF(struct Value *value)
{
  if (DIRECTMODE)
    {
      return Value_new_ERROR(value, NOTINDIRECTMODE);
    }

  if (g_pass == INTERPRET)
    {
      g_stack.onerror.line = -1;
    }

  ++g_pc.token;
  return (struct Value *)0;
}

struct Value *stmt_OPEN(struct Value *value)
{
  int inout = -1, append = 0;
  int mode = FS_ACCESS_NONE, lock = FS_LOCK_NONE;
  long int channel;
  long int recLength = -1;
  struct Pc errpc;
  struct Value recLengthValue;
  struct Pc statementpc = g_pc;

  ++g_pc.token;
  errpc = g_pc;
  if (eval(value, _("mode or file"))->type == V_ERROR ||
      Value_retype(value, V_STRING)->type == V_ERROR)
    {
      return value;
    }

  if (g_pc.token->type == T_COMMA)        /* parse MBASIC syntax */
    {
      if (value->u.string.length >= 1)
        {
          switch (tolower(value->u.string.character[0]))
            {
            case 'i':
              inout = 0;
              mode = FS_ACCESS_READ;
              break;

            case 'o':
              inout = 1;
              mode = FS_ACCESS_WRITE;
              break;

            case 'a':
              inout = 1;
              mode = FS_ACCESS_WRITE;
              append = 1;
              break;

            case 'r':
              inout = 3;
              mode = FS_ACCESS_READWRITE;
              break;
            }
        }

      Value_destroy(value);
      if (g_pass == INTERPRET && inout == -1)
        {
          g_pc = errpc;
          return Value_new_ERROR(value, BADMODE);
        }

      if (g_pc.token->type != T_COMMA)
        {
          return Value_new_ERROR(value, MISSINGCOMMA);
        }

      ++g_pc.token;
      if (g_pc.token->type == T_CHANNEL)
        {
          ++g_pc.token;
        }

      errpc = g_pc;
      if (eval(value, _("channel"))->type == V_ERROR ||
          Value_retype(value, V_INTEGER)->type == V_ERROR)
        {
          g_pc = errpc;
          return value;
        }

      channel = value->u.integer;
      Value_destroy(value);
      if (g_pass == INTERPRET && channel < 0)
        {
          return Value_new_ERROR(value, OUTOFRANGE, _("channel"));
        }

      if (g_pc.token->type != T_COMMA)
        {
          return Value_new_ERROR(value, MISSINGCOMMA);
        }

      ++g_pc.token;
      if (eval(value, _("file name"))->type == V_ERROR ||
          Value_retype(value, V_STRING)->type == V_ERROR)
        {
          return value;
        }

      if (inout == 3)
        {
          if (g_pc.token->type != T_COMMA)
            {
              Value_destroy(value);
              return Value_new_ERROR(value, MISSINGCOMMA);
            }

          ++g_pc.token;
          errpc = g_pc;
          if (eval(&recLengthValue, _("record length"))->type == V_ERROR ||
              Value_retype(&recLengthValue, V_INTEGER)->type == V_ERROR)
            {
              Value_destroy(value);
              *value = recLengthValue;
              return value;
            }

          recLength = recLengthValue.u.integer;
          Value_destroy(&recLengthValue);
          if (g_pass == INTERPRET && recLength <= 0)
            {
              Value_destroy(value);
              g_pc = errpc;
              return Value_new_ERROR(value, OUTOFRANGE, _("record length"));
            }
        }
    }
  else                          /* parse ANSI syntax */
    {
      struct Value channelValue;
      int newMode;

      switch (g_pc.token->type)
        {
        case T_FOR_INPUT:
          inout = 0;
          mode = FS_ACCESS_READ;
          ++g_pc.token;
          break;

        case T_FOR_OUTPUT:
          inout = 1;
          mode = FS_ACCESS_WRITE;
          ++g_pc.token;
          break;

        case T_FOR_APPEND:
          inout = 1;
          mode = FS_ACCESS_WRITE;
          append = 1;
          ++g_pc.token;
          break;

        case T_FOR_RANDOM:
          inout = 3;
          mode = FS_ACCESS_READWRITE;
          ++g_pc.token;
          break;

        case T_FOR_BINARY:
          inout = 4;
          mode = FS_ACCESS_READWRITE;
          ++g_pc.token;
          break;

        default:
          inout = 3;
          mode = FS_ACCESS_READWRITE;
          break;
        }

      switch (g_pc.token->type)
        {
        case T_ACCESS_READ:
          newMode = FS_ACCESS_READ;
          break;

        case T_ACCESS_READ_WRITE:
          newMode = FS_ACCESS_READWRITE;
          break;

        case T_ACCESS_WRITE:
          newMode = FS_ACCESS_WRITE;
          break;

        default:
          newMode = FS_ACCESS_NONE;
        }

      if (newMode != FS_ACCESS_NONE)
        {
          if ((newMode & mode) == 0)
            {
              return Value_new_ERROR(value, WRONGMODE);
            }

          mode = newMode;
          ++g_pc.token;
        }

      switch (g_pc.token->type)
        {
        case T_SHARED:
          lock = FS_LOCK_NONE;
          ++g_pc.token;
          break;

        case T_LOCK_READ:
          lock = FS_LOCK_SHARED;
          ++g_pc.token;
          break;

        case T_LOCK_WRITE:
          lock = FS_LOCK_EXCLUSIVE;
          ++g_pc.token;
          break;

        default:;
        }

      if (g_pc.token->type != T_AS)
        {
          Value_destroy(value);
          return Value_new_ERROR(value, MISSINGAS);
        }

      ++g_pc.token;
      if (g_pc.token->type == T_CHANNEL)
        {
          ++g_pc.token;
        }

      errpc = g_pc;
      if (eval(&channelValue, _("channel"))->type == V_ERROR ||
          Value_retype(&channelValue, V_INTEGER)->type == V_ERROR)
        {
          g_pc = errpc;
          Value_destroy(value);
          *value = channelValue;
          return value;
        }

      channel = channelValue.u.integer;
      Value_destroy(&channelValue);
      if (inout == 3)
        {
          if (g_pc.token->type == T_IDENTIFIER)
            {
              if (cistrcmp(g_pc.token->u.identifier->name, "len"))
                {
                  Value_destroy(value);
                  return Value_new_ERROR(value, MISSINGLEN);
                }

              ++g_pc.token;
              if (g_pc.token->type != T_EQ)
                {
                  Value_destroy(value);
                  return Value_new_ERROR(value, MISSINGEQ);
                }

              ++g_pc.token;
              errpc = g_pc;
              if (eval(&recLengthValue, _("record length"))->type == V_ERROR ||
                  Value_retype(&recLengthValue, V_INTEGER)->type == V_ERROR)
                {
                  Value_destroy(value);
                  *value = recLengthValue;
                  return value;
                }

              recLength = recLengthValue.u.integer;
              Value_destroy(&recLengthValue);
              if (g_pass == INTERPRET && recLength <= 0)
                {
                  Value_destroy(value);
                  g_pc = errpc;
                  return Value_new_ERROR(value, OUTOFRANGE, _("record length"));
                }
            }
          else
            {
              recLength = 1;
            }
        }
    }

  /* open file with name value */
  if (g_pass == INTERPRET)
    {
      int res = -1;

      if (inout == 0)
        {
          res = FS_openinChn(channel, value->u.string.character, mode);
        }
      else if (inout == 1)
        {
          res = FS_openoutChn(channel, value->u.string.character, mode, append);
        }
      else if (inout == 3)
        {
          res =
            FS_openrandomChn(channel, value->u.string.character, mode,
                             recLength);
        }
      else if (inout == 4)
        {
          res = FS_openbinaryChn(channel, value->u.string.character, mode);
        }

      if (res == -1)
        {
          g_pc = statementpc;
          Value_destroy(value);
          return Value_new_ERROR(value, IOERROR, FS_errmsg);
        }
      else
        {
          if (lock != FS_LOCK_NONE && FS_lock(channel, 0, 0, lock, 0) == -1)
            {
              g_pc = statementpc;
              Value_destroy(value);
              Value_new_ERROR(value, IOERROR, FS_errmsg);
              FS_close(channel);
              return value;
            }
        }
    }

  Value_destroy(value);
  return (struct Value *)0;
}

struct Value *stmt_OPTIONBASE(struct Value *value)
{
  ++g_pc.token;
  if (eval(value, _("array subscript base"))->type == V_ERROR ||
      (g_pass != DECLARE && Value_retype(value, V_INTEGER)->type == V_ERROR))
    {
      return value;
    }

  if (g_pass == INTERPRET)
    {
      g_optionbase = value->u.integer;
    }

  Value_destroy(value);
  return (struct Value *)0;
}

struct Value *stmt_OPTIONRUN(struct Value *value)
{
  ++g_pc.token;
  if (g_pass == INTERPRET)
    {
      FS_xonxoff(STDCHANNEL, 0);
    }

  return (struct Value *)0;
}

struct Value *stmt_OPTIONSTOP(struct Value *value)
{
  ++g_pc.token;
  if (g_pass == INTERPRET)
    {
      FS_xonxoff(STDCHANNEL, 1);
    }

  return (struct Value *)0;
}

struct Value *stmt_OUT_POKE(struct Value *value)
{
  int out, address, val;
  struct Pc lpc;

  out = (g_pc.token->type == T_OUT);
  lpc = g_pc;
  ++g_pc.token;
  if (eval(value, _("address"))->type == V_ERROR ||
      Value_retype(value, V_INTEGER)->type == V_ERROR)
    {
      return value;
    }

  address = value->u.integer;
  Value_destroy(value);
  if (g_pc.token->type != T_COMMA)
    {
      return Value_new_ERROR(value, MISSINGCOMMA);
    }

  ++g_pc.token;
  if (eval(value, _("output value"))->type == V_ERROR ||
      Value_retype(value, V_INTEGER)->type == V_ERROR)
    {
      return value;
    }

  val = value->u.integer;
  Value_destroy(value);
  if (g_pass == INTERPRET)
    {
      if ((out ? FS_portOutput : FS_memOutput) (address, val) == -1)
        {
          g_pc = lpc;
          return Value_new_ERROR(value, IOERROR, FS_errmsg);
        }
    }

  return (struct Value *)0;
}

struct Value *stmt_PRINT_LPRINT(struct Value *value)
{
  int nl = 1;
  int chn = (g_pc.token->type == T_PRINT ? STDCHANNEL : LPCHANNEL);
  int printusing = 0;
  struct Value usingval;
  struct String *using = (struct String *)0;
  size_t usingpos = 0;

  ++g_pc.token;
  if (chn == STDCHANNEL && g_pc.token->type == T_CHANNEL)
    {
      ++g_pc.token;
      if (eval(value, _("channel"))->type == V_ERROR ||
          Value_retype(value, V_INTEGER)->type == V_ERROR)
        {
          return value;
        }

      chn = value->u.integer;
      Value_destroy(value);
      if (g_pc.token->type == T_COMMA)
        {
          ++g_pc.token;
        }
    }

  if (g_pc.token->type == T_USING)
    {
      struct Pc usingpc;

      usingpc = g_pc;
      printusing = 1;
      ++g_pc.token;
      if (g_pc.token->type == T_INTEGER)
        {
          if (g_pass == COMPILE &&
              Program_imageLine(&g_program, g_pc.token->u.integer,
                                &usingpc.token->u.image) == (struct Pc *)0)
            {
              return Value_new_ERROR(value, NOSUCHIMAGELINE);
            }
          else if (g_pass == INTERPRET)
            {
              using = usingpc.token->u.image.token->u.string;
            }

          Value_new_STRING(&usingval);
          ++g_pc.token;
        }
      else
        {
          if (eval(&usingval, _("format string"))->type == V_ERROR ||
              Value_retype(&usingval, V_STRING)->type == V_ERROR)
            {
              *value = usingval;
              return value;
            }

          using = &usingval.u.string;
        }

      if (g_pc.token->type != T_SEMICOLON)
        {
          Value_destroy(&usingval);
          return Value_new_ERROR(value, MISSINGSEMICOLON);
        }

      ++g_pc.token;
    }
  else
    {
      Value_new_STRING(&usingval);
      using = &usingval.u.string;
    }

  while (1)
    {
      struct Pc valuepc;

      valuepc = g_pc;
      if (eval(value, (const char *)0))
        {
          if (value->type == V_ERROR)
            {
              Value_destroy(&usingval);
              return value;
            }

          if (g_pass == INTERPRET)
            {
              struct String s;

              String_new(&s);
              if (Value_toStringUsing(value, &s, using, &usingpos)->type ==
                  V_ERROR)
                {
                  Value_destroy(&usingval);
                  String_destroy(&s);
                  g_pc = valuepc;
                  return value;
                }

              if (FS_putItem(chn, &s) == -1)
                {
                  Value_destroy(&usingval);
                  Value_destroy(value);
                  String_destroy(&s);
                  return Value_new_ERROR(value, IOERROR, FS_errmsg);
                }

              String_destroy(&s);
            }

          Value_destroy(value);
          nl = 1;
        }
      else if (g_pc.token->type == T_TAB || g_pc.token->type == T_SPC)
        {
          int tab = g_pc.token->type == T_TAB;

          ++g_pc.token;
          if (g_pc.token->type != T_OP)
            {
              Value_destroy(&usingval);
              return Value_new_ERROR(value, MISSINGOP);
            }

          ++g_pc.token;
          if (eval(value, _("count"))->type == V_ERROR ||
              Value_retype(value, V_INTEGER)->type == V_ERROR)
            {
              Value_destroy(&usingval);
              return value;
            }

          if (g_pass == INTERPRET)
            {
              int s = value->u.integer;
              int r = 0;

              if (tab)
                {
                  r = FS_tab(chn, s);
                }
              else
                {
                  while (s-- > 0 && (r = FS_putChar(chn, ' ')) != -1);
                }

              if (r == -1)
                {
                  Value_destroy(&usingval);
                  Value_destroy(value);
                  return Value_new_ERROR(value, IOERROR, FS_errmsg);
                }
            }

          Value_destroy(value);
          if (g_pc.token->type != T_CP)
            {
              Value_destroy(&usingval);
              return Value_new_ERROR(value, MISSINGCP);
            }

          ++g_pc.token;
          nl = 1;
        }

      else if (g_pc.token->type == T_SEMICOLON)
        {
          ++g_pc.token;
          nl = 0;
        }

      else if (g_pc.token->type == T_COMMA)
        {
          ++g_pc.token;
          if (g_pass == INTERPRET && !printusing)
            {
              FS_nextcol(chn);
            }

          nl = 0;
        }

      else
        {
          break;
        }

      if (g_pass == INTERPRET && FS_flush(chn) == -1)
        {
          Value_destroy(&usingval);
          return Value_new_ERROR(value, IOERROR, FS_errmsg);
        }
    }

  Value_destroy(&usingval);
  if (g_pass == INTERPRET)
    {
      if (nl && FS_putChar(chn, '\n') == -1)
        {
          return Value_new_ERROR(value, IOERROR, FS_errmsg);
        }

      if (FS_flush(chn) == -1)
        {
          return Value_new_ERROR(value, IOERROR, FS_errmsg);
        }
    }

  return (struct Value *)0;
}

struct Value *stmt_RANDOMIZE(struct Value *value)
{
  struct Pc argpc;

  ++g_pc.token;
  argpc = g_pc;
  if (eval(value, (const char *)0))
    {
      Value_retype(value, V_INTEGER);
      if (value->type == V_ERROR)
        {
          g_pc = argpc;
          Value_destroy(value);
          return Value_new_ERROR(value, MISSINGEXPR,
                                 _("random number generator seed"));
        }

      if (g_pass == INTERPRET)
        {
          srand(g_pc.token->u.integer);
        }

      Value_destroy(value);
    }
  else
    {
      srand(getpid() ^ time((time_t *) 0));
    }

  return (struct Value *)0;
}

struct Value *stmt_READ(struct Value *value)
{
  ++g_pc.token;
  while (1)
    {
      struct Value *l;
      struct Pc lvaluepc;

      lvaluepc = g_pc;
      if (g_pc.token->type != T_IDENTIFIER)
        {
          return Value_new_ERROR(value, MISSINGREADIDENT);
        }

      if (g_pass == DECLARE &&
          Global_variable(&g_globals, g_pc.token->u.identifier,
                          g_pc.token->u.identifier->defaultType,
                          (g_pc.token + 1)->type ==
                          T_OP ? GLOBALARRAY : GLOBALVAR, 0) == 0)
        {
          return Value_new_ERROR(value, REDECLARATION);
        }

      if ((l = lvalue(value))->type == V_ERROR)
        {
          return value;
        }

      if (g_pass == INTERPRET && dataread(value, l))
        {
          g_pc = lvaluepc;
          return value;
        }

      if (g_pc.token->type == T_COMMA)
        {
          ++g_pc.token;
        }
      else
        {
          break;
        }
    }

  return (struct Value *)0;
}

struct Value *stmt_COPY_RENAME(struct Value *value)
{
  struct Pc argpc;
  struct Value from;
  struct Pc statementpc = g_pc;

  ++g_pc.token;
  argpc = g_pc;
  if (eval(&from, _("source file"))->type == V_ERROR ||
      (g_pass != DECLARE && Value_retype(&from, V_STRING)->type == V_ERROR))
    {
      g_pc = argpc;
      *value = from;
      return value;
    }

  if (g_pc.token->type != T_TO)
    {
      Value_destroy(&from);
      return Value_new_ERROR(value, MISSINGTO);
    }

  ++g_pc.token;
  argpc = g_pc;
  if (eval(value, _("destination file"))->type == V_ERROR ||
      (g_pass != DECLARE && Value_retype(value, V_STRING)->type == V_ERROR))
    {
      g_pc = argpc;
      return value;
    }

  if (g_pass == INTERPRET)
    {
      const char *msg;
      int res;

      if (statementpc.token->type == T_RENAME)
        {
          res = rename(from.u.string.character, value->u.string.character);
          msg = strerror(errno);
        }
      else
        {
          res = FS_copy(from.u.string.character, value->u.string.character);
          msg = FS_errmsg;
        }

      if (res == -1)
        {
          Value_destroy(&from);
          Value_destroy(value);
          g_pc = statementpc;
          return Value_new_ERROR(value, IOERROR, msg);
        }
    }

  Value_destroy(&from);
  Value_destroy(value);
  return (struct Value *)0;
}

struct Value *stmt_RENUM(struct Value *value)
{
  int first = 10, inc = 10;

  ++g_pc.token;
  if (g_pc.token->type == T_INTEGER)
    {
      first = g_pc.token->u.integer;
      ++g_pc.token;
      if (g_pc.token->type == T_COMMA)
        {
          ++g_pc.token;
          if (g_pc.token->type != T_INTEGER)
            return Value_new_ERROR(value, MISSINGINCREMENT);
          inc = g_pc.token->u.integer;
          ++g_pc.token;
        }
    }

  if (g_pass == INTERPRET)
    {
      if (!DIRECTMODE)
        {
          return Value_new_ERROR(value, NOTINPROGRAMMODE);
        }

      Program_renum(&g_program, first, inc);
    }

  return (struct Value *)0;
}

struct Value *stmt_REPEAT(struct Value *value)
{
  if (g_pass == DECLARE || g_pass == COMPILE)
    {
      pushLabel(L_REPEAT, &g_pc);
    }

  ++g_pc.token;
  return (struct Value *)0;
}

struct Value *stmt_RESTORE(struct Value *value)
{
  struct Token *restorepc = g_pc.token;

  if (g_pass == INTERPRET)
    {
      g_curdata = g_pc.token->u.restore;
    }

  ++g_pc.token;
  if (g_pc.token->type == T_INTEGER)
    {
      if (g_pass == COMPILE &&
          Program_dataLine(&g_program, g_pc.token->u.integer,
                           &restorepc->u.restore) == (struct Pc *)0)
        {
          return Value_new_ERROR(value, NOSUCHDATALINE);
        }

      ++g_pc.token;
    }
  else if (g_pass == COMPILE)
    {
      restorepc->u.restore = g_stack.begindata;
    }

  return (struct Value *)0;
}

struct Value *stmt_RETURN(struct Value *value)
{
  if (g_pass == DECLARE || g_pass == COMPILE)
    {
      ++g_pc.token;
    }

  if (g_pass == INTERPRET)
    {
      if (Auto_gosubReturn(&g_stack, &g_pc))
        {
          Program_trace(&g_program, &g_pc, 0, 1);
        }
      else
        {
          return Value_new_ERROR(value, STRAYRETURN);
        }
    }

  return (struct Value *)0;
}

struct Value *stmt_RUN(struct Value *value)
{
  struct Pc argpc, begin;

  g_stack.resumeable = 0;
  ++g_pc.token;
  argpc = g_pc;
  if (g_pc.token->type == T_INTEGER)
    {
      if (Program_goLine(&g_program, g_pc.token->u.integer, &begin) ==
          (struct Pc *)0)
        {
          return Value_new_ERROR(value, NOSUCHLINE);
        }

      if (g_pass == COMPILE &&
          Program_scopeCheck(&g_program, &begin, findLabel(L_FUNC)))
        {
          return Value_new_ERROR(value, OUTOFSCOPE);
        }

      ++g_pc.token;
    }
  else if (eval(value, (const char *)0))
    {
      if (value->type == V_ERROR ||
          Value_retype(value, V_STRING)->type == V_ERROR)
        {
          g_pc = argpc;
          return value;
        }
      else if (g_pass == INTERPRET)
        {
          int chn;
          struct Program newprogram;

          if ((chn = FS_openin(value->u.string.character)) == -1)
            {
              g_pc = argpc;
              Value_destroy(value);
              return Value_new_ERROR(value, IOERROR, FS_errmsg);
            }

          Value_destroy(value);
          Program_new(&newprogram);
          if (Program_merge(&newprogram, chn, value))
            {
              g_pc = argpc;
              Program_destroy(&newprogram);
              return value;
            }

          FS_close(chn);
          new();
          Program_destroy(&g_program);
          g_program = newprogram;
          if (Program_beginning(&g_program, &begin) == (struct Pc *)0)
            {
              return Value_new_ERROR(value, NOPROGRAM);
            }
        }
      else
        {
          Value_destroy(value);
        }
    }
  else
    {
      if (Program_beginning(&g_program, &begin) == (struct Pc *)0)
        {
          return Value_new_ERROR(value, NOPROGRAM);
        }
    }

  if (g_pass == INTERPRET)
    {
      if (compileProgram(value, 1)->type == V_ERROR)
        {
          return value;
        }

      g_pc = begin;
      g_curdata = g_stack.begindata;
      Global_clear(&g_globals);
      FS_closefiles();
      Program_trace(&g_program, &g_pc, 0, 1);
    }

  return (struct Value *)0;
}

struct Value *stmt_SAVE(struct Value *value)
{
  struct Pc loadpc;
  int name;

  if (g_pass == INTERPRET && !DIRECTMODE)
    {
      return Value_new_ERROR(value, NOTINPROGRAMMODE);
    }

  ++g_pc.token;
  loadpc = g_pc;
  if (g_pc.token->type == T_EOL && g_program.name.length)
    {
      name = 0;
    }
  else
    {
      name = 1;
      if (eval(value, _("file name"))->type == V_ERROR ||
          Value_retype(value, V_STRING)->type == V_ERROR)
        {
          g_pc = loadpc;
          return value;
        }
    }

  if (g_pass == INTERPRET)
    {
      int chn;

      if (name)
        {
          Program_setname(&g_program, value->u.string.character);
        }

      if ((chn = FS_openout(g_program.name.character)) == -1)
        {
          g_pc = loadpc;
          if (name)
            {
              Value_destroy(value);
            }

          return Value_new_ERROR(value, IOERROR, FS_errmsg);
        }

      FS_width(chn, 0);
      if (name)
        {
          Value_destroy(value);
        }

      if (Program_list(&g_program, chn, 0, (struct Pc *)0, (struct Pc *)0, value))
        {
          g_pc = loadpc;
          return value;
        }

      FS_close(chn);
      g_program.unsaved = 0;
    }
  else if (name)
    {
      Value_destroy(value);
    }

  return (struct Value *)0;
}

struct Value *stmt_SELECTCASE(struct Value *value)
{
  struct Pc statementpc = g_pc;

  if (g_pass == DECLARE || g_pass == COMPILE)
    {
      pushLabel(L_SELECTCASE, &g_pc);
    }

  ++g_pc.token;
  if (eval(value, _("selector"))->type == V_ERROR)
    {
      return value;
    }

  if (g_pass == DECLARE || g_pass == COMPILE)
    {
      statementpc.token->u.selectcase->type = value->type;
      statementpc.token->u.selectcase->nextcasevalue.line = -1;
    }
  else
    {
      struct Pc casevaluepc;
      int match = 0;

      g_pc = casevaluepc = statementpc.token->u.selectcase->nextcasevalue;
      do
        {
          ++g_pc.token;
          switch (casevaluepc.token->type)
            {
            case T_CASEVALUE:
              {
                do
                  {
                    struct Value casevalue1;

                    if (g_pc.token->type == T_IS)
                      {
                        enum TokenType relop;

                        ++g_pc.token;
                        relop = g_pc.token->type;
                        ++g_pc.token;
                        if (eval(&casevalue1, "`is'")->type == V_ERROR)
                          {
                            Value_destroy(value);
                            *value = casevalue1;
                            return value;
                          }

                        Value_retype(&casevalue1,
                                     statementpc.token->u.selectcase->type);
                        assert(casevalue1.type != V_ERROR);
                        if (!match)
                          {
                            struct Value cmp;

                            Value_clone(&cmp, value);
                            switch (relop)
                              {
                              case T_LT:
                                Value_lt(&cmp, &casevalue1, 1);
                                break;

                              case T_LE:
                                Value_le(&cmp, &casevalue1, 1);
                                break;

                              case T_EQ:
                                Value_eq(&cmp, &casevalue1, 1);
                                break;
                              case T_GE:
                                Value_ge(&cmp, &casevalue1, 1);
                                break;

                              case T_GT:
                                Value_gt(&cmp, &casevalue1, 1);
                                break;
                              case T_NE:
                                Value_ne(&cmp, &casevalue1, 1);
                                break;

                              default:
                                assert(0);
                              }

                            assert(cmp.type == V_INTEGER);
                            match = cmp.u.integer;
                            Value_destroy(&cmp);
                          }

                        Value_destroy(&casevalue1);
                      }
                    else
                      {
                        if (eval(&casevalue1, "`case'")->type == V_ERROR)
                          {
                            Value_destroy(value);
                            *value = casevalue1;
                            return value;
                          }

                        Value_retype(&casevalue1,
                                     statementpc.token->u.selectcase->type);
                        assert(casevalue1.type != V_ERROR);
                        if (g_pc.token->type == T_TO)     /* match range */
                          {
                            struct Value casevalue2;

                            ++g_pc.token;
                            if (eval(&casevalue2, "`case'")->type == V_ERROR)
                              {
                                Value_destroy(&casevalue1);
                                Value_destroy(value);
                                *value = casevalue2;
                                return value;
                              }

                            Value_retype(&casevalue2,
                                         statementpc.token->u.selectcase->type);
                            assert(casevalue2.type != V_ERROR);
                            if (!match)
                              {
                                struct Value cmp1, cmp2;

                                Value_clone(&cmp1, value);
                                Value_clone(&cmp2, value);
                                Value_ge(&cmp1, &casevalue1, 1);
                                assert(cmp1.type == V_INTEGER);
                                Value_le(&cmp2, &casevalue2, 1);
                                assert(cmp2.type == V_INTEGER);
                                match = cmp1.u.integer && cmp2.u.integer;
                                Value_destroy(&cmp1);
                                Value_destroy(&cmp2);
                              }

                            Value_destroy(&casevalue2);
                          }
                        else    /* match value */
                          {
                            if (!match)
                              {
                                struct Value cmp;

                                Value_clone(&cmp, value);
                                Value_eq(&cmp, &casevalue1, 1);
                                assert(cmp.type == V_INTEGER);
                                match = cmp.u.integer;
                                Value_destroy(&cmp);
                              }
                          }

                        Value_destroy(&casevalue1);
                      }

                    if (g_pc.token->type == T_COMMA)
                      {
                        ++g_pc.token;
                      }
                    else
                      {
                        break;
                      }
                  }
                while (1);

                break;
              }

            case T_CASEELSE:
              {
                match = 1;
                break;
              }

            default:
              assert(0);
            }

          if (!match)
            {
              if (casevaluepc.token->u.casevalue->nextcasevalue.line != -1)
                {
                  g_pc = casevaluepc =
                    casevaluepc.token->u.casevalue->nextcasevalue;
                }
              else
                {
                  g_pc = statementpc.token->u.selectcase->endselect;
                  break;
                }
            }
        }
      while (!match);
    }

  Value_destroy(value);
  return (struct Value *)0;
}

struct Value *stmt_SHELL(struct Value *value)
{
#if defined(CONFIG_EXAMPLES_BAS_SHELL) && defined(CONFIG_ARCH_HAVE_VFORK)
  pid_t pid;
  int status;

  ++g_pc.token;
  if (eval(value, (const char *)0))
    {
      if (value->type == V_ERROR ||
          Value_retype(value, V_STRING)->type == V_ERROR)
        {
          return value;
        }

      if (g_pass == INTERPRET)
        {
          if (g_run_restricted)
            {
              Value_destroy(value);
              return Value_new_ERROR(value, RESTRICTED, strerror(errno));
            }

          FS_shellmode(STDCHANNEL);
          switch (pid = vfork())
            {
            case -1:
              {
                FS_fsmode(STDCHANNEL);
                Value_destroy(value);
                return Value_new_ERROR(value, FORKFAILED, strerror(errno));
              }

            case 0:
              {
                execl("/bin/sh", "sh", "-c", value->u.string.character,
                      (const char *)0);
                exit(127);
              }

            default:
              {
                /* Wait for the shell to complete */

                while (waitpid(pid, &status, 0) < 0 && errno != EINTR);
              }
            }

          FS_fsmode(STDCHANNEL);
        }

      Value_destroy(value);
    }
  else
    {
      if (g_pass == INTERPRET)
        {
          if (g_run_restricted)
            {
              return Value_new_ERROR(value, RESTRICTED, strerror(errno));
            }

          FS_shellmode(STDCHANNEL);
          switch (pid = vfork())
            {
            case -1:
              {
                FS_fsmode(STDCHANNEL);
                return Value_new_ERROR(value, FORKFAILED, strerror(errno));
              }

            case 0:
              {
                const char *shell;

                shell = getenv("SHELL");
                if (shell == (const char *)0)
                  {
                    shell = "/bin/sh";
                  }

                execl(shell,
                      (strrchr(shell, '/') ? strrchr(shell, '/') + 1 : shell),
                      (const char *)0);
                exit(127);
              }

            default:
              {
                /* Wait for the shell to complete */

                while (waitpid(pid, &status, 0) < 0 && errno != EINTR);
              }
            }

          FS_fsmode(STDCHANNEL);
        }
    }

  return (struct Value *)0;
#else
  return Value_new_ERROR(value, NOTAVAILABLE, strerror(ENOSYS));
#endif
}

struct Value *stmt_SLEEP(struct Value *value)
{
  double s;

  ++g_pc.token;
  if (eval(value, _("pause"))->type == V_ERROR ||
      Value_retype(value, V_REAL)->type == V_ERROR)
    {
      return value;
    }

  s = value->u.real;
  Value_destroy(value);
  if (g_pass == INTERPRET)
    {
      if (s < 0.0)
        {
          return Value_new_ERROR(value, OUTOFRANGE, _("pause"));
        }

      FS_sleep(s);
    }

  return (struct Value *)0;
}

struct Value *stmt_STOP(struct Value *value)
{
  if (g_pass != INTERPRET)
    {
      ++g_pc.token;
    }

  return (struct Value *)0;
}

struct Value *stmt_SUBEXIT(struct Value *value)
{
  struct Pc *curfn = (struct Pc *)0;

  if (g_pass == DECLARE || g_pass == COMPILE)
    {
      if ((curfn = findLabel(L_FUNC)) == (struct Pc *)0 ||
          (curfn->token + 1)->u.identifier->defaultType != V_VOID)
        {
          return Value_new_ERROR(value, STRAYSUBEXIT);
        }
    }

  ++g_pc.token;
  if (g_pass == INTERPRET)
    {
      return Value_new_VOID(value);
    }

  return (struct Value *)0;
}

struct Value *stmt_SWAP(struct Value *value)
{
  struct Value *l1, *l2;
  struct Pc lvaluepc;

  ++g_pc.token;
  lvaluepc = g_pc;
  if (g_pc.token->type != T_IDENTIFIER)
    {
      return Value_new_ERROR(value, MISSINGSWAPIDENT);
    }

  if (g_pass == DECLARE &&
      Global_variable(&g_globals, g_pc.token->u.identifier,
                      g_pc.token->u.identifier->defaultType,
                      (g_pc.token + 1)->type == T_OP ? GLOBALARRAY : GLOBALVAR,
                      0) == 0)
    {
      return Value_new_ERROR(value, REDECLARATION);
    }

  if ((l1 = lvalue(value))->type == V_ERROR)
    {
      return value;
    }

  if (g_pc.token->type == T_COMMA)
    {
      ++g_pc.token;
    }
  else
    {
      return Value_new_ERROR(value, MISSINGCOMMA);
    }

  lvaluepc = g_pc;
  if (g_pc.token->type != T_IDENTIFIER)
    {
      return Value_new_ERROR(value, MISSINGSWAPIDENT);
    }

  if (g_pass == DECLARE &&
      Global_variable(&g_globals, g_pc.token->u.identifier,
                      g_pc.token->u.identifier->defaultType,
                      (g_pc.token + 1)->type == T_OP ? GLOBALARRAY : GLOBALVAR,
                      0) == 0)
    {
      return Value_new_ERROR(value, REDECLARATION);
    }

  if ((l2 = lvalue(value))->type == V_ERROR)
    {
      return value;
    }

  if (l1->type != l2->type)
    {
      g_pc = lvaluepc;
      return Value_new_typeError(value, l2->type, l1->type);
    }

  if (g_pass == INTERPRET)
    {
      struct Value foo;

      foo = *l1;
      *l1 = *l2;
      *l2 = foo;
    }

  return (struct Value *)0;
}

struct Value *stmt_SYSTEM(struct Value *value)
{
  ++g_pc.token;
  if (g_pass == INTERPRET)
    {
      if (g_program.unsaved)
        {
          int ch;

          FS_putChars(STDCHANNEL, _("Quit without saving? (y/n) "));
          FS_flush(STDCHANNEL);
          if ((ch = FS_getChar(STDCHANNEL)) != -1)
            {
              FS_putChar(STDCHANNEL, ch);
              FS_flush(STDCHANNEL);
              FS_nextline(STDCHANNEL);
              if (tolower(ch) == *_("yes"))
                {
                  bas_exit();
                  exit(0);
                }
            }
        }
      else
        {
          bas_exit();
          exit(0);
        }
    }

  return (struct Value *)0;
}

struct Value *stmt_TROFF(struct Value *value)
{
  ++g_pc.token;
  g_program.trace = 0;
  return (struct Value *)0;
}

struct Value *stmt_TRON(struct Value *value)
{
  ++g_pc.token;
  g_program.trace = 1;
  return (struct Value *)0;
}

struct Value *stmt_TRUNCATE(struct Value *value)
{
  struct Pc chnpc;
  int chn;

  chnpc = g_pc;
  ++g_pc.token;
  if (g_pc.token->type == T_CHANNEL)
    {
      ++g_pc.token;
    }

  if (eval(value, (const char *)0) == (struct Value *)0)
    {
      return Value_new_ERROR(value, MISSINGEXPR, _("channel"));
    }

  if (value->type == V_ERROR || Value_retype(value, V_INTEGER)->type == V_ERROR)
    {
      return value;
    }

  chn = value->u.integer;
  Value_destroy(value);
  if (g_pass == INTERPRET && FS_truncate(chn) == -1)
    {
      g_pc = chnpc;
      return Value_new_ERROR(value, IOERROR, FS_errmsg);
    }

  return (struct Value *)0;
}

struct Value *stmt_UNNUM(struct Value *value)
{
  ++g_pc.token;
  if (g_pass == INTERPRET)
    {
      if (!DIRECTMODE)
        {
          return Value_new_ERROR(value, NOTINPROGRAMMODE);
        }

      Program_unnum(&g_program);
    }

  return (struct Value *)0;
}

struct Value *stmt_UNTIL(struct Value *value)
{
  struct Pc untilpc = g_pc;
  struct Pc *repeatpc;

  ++g_pc.token;
  if (eval(value, _("condition"))->type == V_ERROR)
    {
      return value;
    }

  if (g_pass == INTERPRET)
    {
      if (Value_isNull(value))
        {
          g_pc = untilpc.token->u.until;
        }

      Value_destroy(value);
    }

  if (g_pass == DECLARE || g_pass == COMPILE)
    {
      if ((repeatpc = popLabel(L_REPEAT)) == (struct Pc *)0)
        {
          return Value_new_ERROR(value, STRAYUNTIL);
        }

      untilpc.token->u.until = *repeatpc;
    }

  return (struct Value *)0;
}

struct Value *stmt_WAIT(struct Value *value)
{
  int address, mask, sel = -1, usesel;
  struct Pc lpc;

  lpc = g_pc;
  ++g_pc.token;
  if (eval(value, _("address"))->type == V_ERROR ||
      Value_retype(value, V_INTEGER)->type == V_ERROR)
    {
      return value;
    }

  address = value->u.integer;
  Value_destroy(value);
  if (g_pc.token->type != T_COMMA)
    {
      return Value_new_ERROR(value, MISSINGCOMMA);
    }

  ++g_pc.token;
  if (eval(value, _("mask"))->type == V_ERROR ||
      Value_retype(value, V_INTEGER)->type == V_ERROR)
    {
      return value;
    }

  mask = value->u.integer;
  Value_destroy(value);
  if (g_pc.token->type == T_COMMA)
    {
      ++g_pc.token;
      if (eval(value, _("select"))->type == V_ERROR ||
          Value_retype(value, V_INTEGER)->type == V_ERROR)
        {
          return value;
        }

      sel = value->u.integer;
      usesel = 1;
      Value_destroy(value);
    }
  else
    {
      usesel = 0;
    }

  if (g_pass == INTERPRET)
    {
      int v;

      do
        {
          if ((v = FS_portInput(address)) == -1)
            {
              g_pc = lpc;
              return Value_new_ERROR(value, IOERROR, FS_errmsg);
            }
        }
      while ((usesel ? (v ^ sel) & mask : v ^ mask) == 0);
    }
  return (struct Value *)0;
}

struct Value *stmt_WHILE(struct Value *value)
{
  struct Pc whilepc = g_pc;

  if (g_pass == DECLARE || g_pass == COMPILE)
    {
      pushLabel(L_WHILE, &g_pc);
    }

  ++g_pc.token;
  if (eval(value, _("condition"))->type == V_ERROR)
    {
      return value;
    }

  if (g_pass == INTERPRET)
    {
      if (Value_isNull(value))
        {
          g_pc = *whilepc.token->u.afterwend;
        }

      Value_destroy(value);
    }

  return (struct Value *)0;
}

struct Value *stmt_WEND(struct Value *value)
{
  if (g_pass == DECLARE || g_pass == COMPILE)
    {
      struct Pc *whilepc;

      if ((whilepc = popLabel(L_WHILE)) == (struct Pc *)0)
        {
          return Value_new_ERROR(value, STRAYWEND, topLabelDescription());
        }

      *g_pc.token->u.whilepc = *whilepc;
      ++g_pc.token;
      *(whilepc->token->u.afterwend) = g_pc;
    }
  else
    {
      g_pc = *g_pc.token->u.whilepc;
    }

  return (struct Value *)0;
}

struct Value *stmt_WIDTH(struct Value *value)
{
  int chn = STDCHANNEL, width;

  ++g_pc.token;
  if (g_pc.token->type == T_CHANNEL)
    {
      ++g_pc.token;
      if (eval(value, _("channel"))->type == V_ERROR ||
          Value_retype(value, V_INTEGER)->type == V_ERROR)
        {
          return value;
        }

      chn = value->u.integer;
      Value_destroy(value);
      if (g_pc.token->type == T_COMMA)
        {
          ++g_pc.token;
        }
    }

  if (eval(value, (const char *)0))
    {
      if (value->type == V_ERROR ||
          Value_retype(value, V_INTEGER)->type == V_ERROR)
        {
          return value;
        }

      width = value->u.integer;
      Value_destroy(value);
      if (g_pass == INTERPRET && FS_width(chn, width) == -1)
        {
          return Value_new_ERROR(value, IOERROR, FS_errmsg);
        }
    }

  if (g_pc.token->type == T_COMMA)
    {
      ++g_pc.token;
      if (eval(value, _("zone width"))->type == V_ERROR ||
          Value_retype(value, V_INTEGER)->type == V_ERROR)
        {
          return value;
        }

      width = value->u.integer;
      Value_destroy(value);
      if (g_pass == INTERPRET && FS_zone(chn, width) == -1)
        {
          return Value_new_ERROR(value, IOERROR, FS_errmsg);
        }
    }

  return (struct Value *)0;
}

struct Value *stmt_WRITE(struct Value *value)
{
  int chn = STDCHANNEL;
  int comma = 0;

  ++g_pc.token;
  if (g_pc.token->type == T_CHANNEL)
    {
      ++g_pc.token;
      if (eval(value, _("channel"))->type == V_ERROR ||
          Value_retype(value, V_INTEGER)->type == V_ERROR)
        {
          return value;
        }

      chn = value->u.integer;
      Value_destroy(value);
      if (g_pc.token->type == T_COMMA)
        {
          ++g_pc.token;
        }
    }

  while (1)
    {
      if (eval(value, (const char *)0))
        {
          if (value->type == V_ERROR)
            {
              return value;
            }

          if (g_pass == INTERPRET)
            {
              struct String s;

              String_new(&s);
              if (comma)
                {
                  String_appendChar(&s, ',');
                }

              if (FS_putString(chn, Value_toWrite(value, &s)) == -1)
                {
                  Value_destroy(value);
                  return Value_new_ERROR(value, IOERROR, FS_errmsg);
                }

              if (FS_flush(chn) == -1)
                {
                  return Value_new_ERROR(value, IOERROR, FS_errmsg);
                }

              String_destroy(&s);
            }

          Value_destroy(value);
          comma = 1;
        }
      else if (g_pc.token->type == T_COMMA || g_pc.token->type == T_SEMICOLON)
        {
          ++g_pc.token;
        }
      else
        {
          break;
        }
    }

  if (g_pass == INTERPRET)
    {
      FS_putChar(chn, '\n');
      if (FS_flush(chn) == -1)
        {
          return Value_new_ERROR(value, IOERROR, FS_errmsg);
        }
    }

  return (struct Value *)0;
}

struct Value *stmt_XREF(struct Value *value)
{
  g_stack.resumeable = 0;
  ++g_pc.token;
  if (g_pass == INTERPRET)
    {
      if (!g_program.runnable && compileProgram(value, 1)->type == V_ERROR)
        {
          return value;
        }

      Program_xref(&g_program, STDCHANNEL);
    }

  return (struct Value *)0;
}

struct Value *stmt_ZONE(struct Value *value)
{
  int chn = STDCHANNEL, width;

  ++g_pc.token;
  if (g_pc.token->type == T_CHANNEL)
    {
      ++g_pc.token;
      if (eval(value, _("channel"))->type == V_ERROR ||
          Value_retype(value, V_INTEGER)->type == V_ERROR)
        {
          return value;
        }

      chn = value->u.integer;
      Value_destroy(value);
      if (g_pc.token->type == T_COMMA)
        {
          ++g_pc.token;
        }
    }

  if (eval(value, _("zone width"))->type == V_ERROR ||
      Value_retype(value, V_INTEGER)->type == V_ERROR)
    {
      return value;
    }

  width = value->u.integer;
  Value_destroy(value);
  if (g_pass == INTERPRET && FS_zone(chn, width) == -1)
    {
      return Value_new_ERROR(value, IOERROR, FS_errmsg);
    }

  return (struct Value *)0;
}
