#include "config.h"

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

#ifdef USE_NLLIBC
#include <nllibc.h>
#endif

#include "const.h"
#include "nlltypes.h"
#include "nlllib.h"
#include "nllmain.h"
#include "formula.h"
#include "command.h"
#include "line.h"
#include "label.h"
#include "value.h"
#include "string.h"
#include "variable.h"
#include "position.h"
#include "array.h"
#include "area.h"
#include "stack.h"
#include "function.h"
#include "key.h"
#include "framebuf.h"
#include "nll.h"

static int stepping = 0;
static int skipping = 0;
static int discarding = 0;

static struct spot break_spot;
static struct spot continue_spot;

static struct {
  int index;
  struct spot spot;
} dread_spot;

static int clear_break(line_t line)
{
  if (!line || (break_spot.line == line)) {
    break_spot.line = NULL;
    break_spot.command = NULL;
  }
  return 0;
}

static int clear_continue(line_t line)
{
  if (!line || (continue_spot.line == line)) {
    continue_spot.line = NULL;
    continue_spot.command = NULL;
  }
  return 0;
}

static int clear_dread(line_t line)
{
  if (!line || (dread_spot.spot.line == line)) {
    dread_spot.spot.line = NULL;
    dread_spot.spot.command = NULL;
    dread_spot.index = 0;
  }
  return 0;
}

static int init_dread(void)
{
  int r;
  line_t line = NULL;

  clear_dread(NULL);

  if ((r = line_get(1, &line)) < 0)
    return r;

  if (line) {
    dread_spot.spot.line    = line;
    dread_spot.spot.command = line->commands;
    dread_spot.index = 0;
  }

  return 0;
}

int nll_clear_line(line_t line)
{
  clear_break(line);
  clear_continue(line);
  clear_dread(line);
  return 0;
}

static int get_continue(spot_t spot)
{
  if (continue_spot.line == NULL)
    return NLL_ERRCODE_NLL_NO_CONTINUE_POINT;
  spot->line    = continue_spot.line;
  spot->command = continue_spot.command;
  return 0;
}

static int set_continue(spot_t spot)
{
  continue_spot.line    = spot->line;
  continue_spot.command = spot->command;
  return 0;
}

static int proc_end(command_arg_t args, int *retcodep)
{
  int r;
  integer_t retcode = 0;
  value_t value;

  if (args[0].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[0].arg.formula.element, &value)) < 0)
      return r;
    if (value) {
      if ((r = value_get_integer(value, &retcode)) < 0)
	return r;
    }
  }

  if (retcodep)
    *retcodep = retcode;

  return 0;
}

static int proc_args(command_arg_t args)
{
  int r, i;

  for (i = 0; i < COMMAND_ARG_MAXNUM; i++) {
    if (args[i].type == COMMAND_ARG_FORMULA) {
      if ((r = formula_proc(args[i].arg.formula.element, NULL)) < 0)
	return r;
    }
  }

  return 0;
}

static int proc_let(command_arg_t args)
{
  int r, i;
  value_t value = NULL, v;

  if (args[0].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[0].arg.formula.element, &value)) < 0)
      return r;
  }

  if (args[1].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[1].arg.formula.element, &v)) < 0)
      return r;
    if (value) {
      if ((r = value_copy_values(&value, v)) < 0)
	;
    }
  }

  for (i = 2; i < COMMAND_ARG_MAXNUM; i++) {
    if (args[i].type == COMMAND_ARG_FORMULA) {
      if ((r = formula_proc(args[i].arg.formula.element, NULL)) < 0)
	return r;
    }
  }

  return 0;
}

static int proc_print(command_arg_t args)
{
  int r, i, printed = 0;
  value_t value;

  for (i = 0; i < COMMAND_ARG_MAXNUM; i++) {
    if (args[i].type == COMMAND_ARG_FORMULA) {
      if ((r = formula_proc(args[i].arg.formula.element, &value)) < 0)
	goto ret;
      if (value) {
	value_output_values(nll_stdout, value, " ");
	nll_wait_output(nll_stdout);
	fputs("\n", nll_stdout);
	printed = 1;
      }
    }
  }

  if (!printed) {
    nll_wait_output(nll_stdout);
    fputs("\n", nll_stdout);
  }

  r = 0;

ret:
  fflush(nll_stdout);

  return r;
}

static int _discard(int num)
{
  int r;
  position_type_t type;

  for (; num > 0; num--) {
    if ((r = position_pop(POSITION_TYPE_NONE, &type, NULL, NULL, NULL)) < 0)
      return r;
    switch (type) {
    case POSITION_TYPE_GOSUB:
    case POSITION_TYPE_LABEL:
    case POSITION_TYPE_EVAL:
      if ((r = stack_pop_type(STACK_TYPE_CALL, NULL)) < 0)
	return r;
      break;
    default:
      break;
    }
  }

  return 0;
}

static int proc_goto(spot_t spot, command_arg_t args)
{
  int r;
  integer_t num;
  value_t value;
  label_t label = NULL;

  if (args[3].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[3].arg.formula.element, &value)) < 0)
      return r;
    if (value) {
      if ((r = value_get_integer(value, &num)) < 0)
	return r;
      if ((r = _discard(num)) < 0)
	return r;
    }
  }

  if (args[0].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[0].arg.formula.element, &value)) < 0)
      return r;
    if (value) {
      if ((r = value_get_label(value, &label)) < 0)
	return r;
    }
  }

  if (!label || !label->spot.line)
    return NLL_ERRCODE_LABEL_NOT_FOUND;

  spot->line    = label->spot.line;
  spot->command = label->spot.command;

  value = NULL;
  if (args[1].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[1].arg.formula.element, &value)) < 0)
      return r;
  }

  if (spot->command->label.args) {
    if ((r = stack_push_type(STACK_TYPE_CALL, value)) < 0)
      return r;
  }

  if (args[2].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[2].arg.formula.element, NULL)) < 0)
      return r;
  }

  return 0;
}

static int proc_gosub(spot_t spot, spot_t current, command_arg_t args)
{
  int r;
  value_t value;
  label_t label = NULL;

  if (args[0].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[0].arg.formula.element, &value)) < 0)
      return r;
    if (value) {
      if ((r = value_get_label(value, &label)) < 0)
	return r;
    }
  }

  if (!label || !label->spot.line)
    return NLL_ERRCODE_LABEL_NOT_FOUND;

  spot->line    = label->spot.line;
  spot->command = label->spot.command;

  value = NULL;
  if (args[1].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[1].arg.formula.element, &value)) < 0)
      return r;
  }

  /* dummy return value for the return from the end of lines */
  if ((r = stack_push_type(STACK_TYPE_CALL, NULL)) < 0)
    return r;

  if (spot->command->label.args) {
    if ((r = stack_push_type(STACK_TYPE_CALL, value)) < 0)
      return r;
  }

  if ((r = position_push(POSITION_TYPE_GOSUB, current, NULL, 0)) < 0)
    return r;

  return 0;
}

static int proc_gonext(spot_t spot, spot_t current, line_t line, command_arg_t args)
{
  int r;
  integer_t n = 1, n2 = -1, search = 0, num;
  value_t value;

  if (args[3].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[3].arg.formula.element, &value)) < 0)
      return r;
    if (value) {
      if ((r = value_get_integer(value, &num)) < 0)
	return r;
      if ((r = _discard(num)) < 0)
	return r;
    }
  }

  if (args[0].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[0].arg.formula.element, &value)) < 0)
      return r;
    if (value) {
      if ((r = value_get_integer(value, &n)) < 0)
	return r;
      search = 1;
    }
  }

  if (args[1].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[1].arg.formula.element, &value)) < 0)
      return r;
    if (value) {
      if ((r = value_get_integer(value, &n2)) < 0)
	return r;
    }
  }

  if (args[2].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[2].arg.formula.element, NULL)) < 0)
      return r;
  }

  if (n2 < 0) {
    n2 = 0;
    if (!search)
      search = 1;
  }

  spot->line    = current->line;
  spot->command = current->command;

  if (search) {
    if (n < 0) {
      for (; spot->line && (n < 0); n++)
	spot->line = spot->line->prev;
    } else {
      for (; spot->line && (n > 0); n--)
	spot->line = spot->line->next;
    }
    spot->command = spot->line ? spot->line->commands : NULL;
  }

  while (n2 > 0) {
    if (!spot->line)
      break;
    if (!spot->command) {
      spot->line = spot->line->next;
      spot->command = spot->line ? spot->line->commands : NULL;
      continue;
    }
    spot->command = spot->command->next;
    n2--;
  }

  return 0;
}

static int proc_return(spot_t spot, command_arg_t args)
{
  int r, retval = 0;
  integer_t num;
  value_t value;
  position_type_t type;

  if (args[2].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[2].arg.formula.element, &value)) < 0)
      return r;
    if (value) {
      if ((r = value_get_integer(value, &num)) < 0)
	return r;
      if ((r = _discard(num)) < 0)
	return r;
    }
  }

  value = NULL;
  if (args[0].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[0].arg.formula.element, &value)) < 0)
      return r;
  }

  /* dummy return value for the return from the end of lines */
  if ((r = stack_pop_type(STACK_TYPE_CALL, NULL)) < 0)
    return r;

  if ((r = stack_push_type(STACK_TYPE_CALL, value)) < 0)
    return r;

  type = position_get_type();

  switch (type) {
  case POSITION_TYPE_GOSUB:
    if ((r = position_pop(POSITION_TYPE_GOSUB, NULL, spot, NULL, NULL)) < 0)
      return r;
    if (spot->line == NULL)
      return NLL_ERRCODE_NLL_NO_CONTINUE_POINT;
    value = NULL;
    if (spot->command->args[2].type == COMMAND_ARG_FORMULA) {
      if ((r = formula_proc(spot->command->args[2].arg.formula.element, &value)) < 0)
	return r;
    }
    if ((r = stack_pop_type(STACK_TYPE_CALL, value)) < 0)
      return r;
    retval = 1;
    break;

  case POSITION_TYPE_LABEL:
    break;

  case POSITION_TYPE_EVAL:
    break;

  default:
    return NLL_ERRCODE_POSITION_DIFFERENT_TYPE;
  }

  if (args[1].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[1].arg.formula.element, NULL)) < 0)
      return r;
  }

  return retval;
}

static int proc_if(command_arg_t args)
{
  int r = 0;
  value_t value;

  if ((r = position_push(POSITION_TYPE_IF, NULL, NULL, discarding)) < 0)
    return r;

  if (!discarding) {
    r = 0;

    if (args[0].type == COMMAND_ARG_FORMULA) {
      if ((r = formula_proc(args[0].arg.formula.element, &value)) < 0)
	return r;
      if ((r = value_is_true(value)) < 0)
	return r;
    }

    discarding = r ? 0 : 1;

    if (!discarding) {
      if (args[1].type == COMMAND_ARG_FORMULA) {
	if ((r = formula_proc(args[1].arg.formula.element, NULL)) < 0)
	  return r;
      }
    } else {
      if (args[2].type == COMMAND_ARG_FORMULA) {
	if ((r = formula_proc(args[2].arg.formula.element, NULL)) < 0)
	  return r;
      }
    }
  }

  return 0;
}

static int proc_else(command_arg_t args)
{
  int r = 0, d;
  value_t value;

  if ((r = position_peek(POSITION_TYPE_IF, NULL, NULL, &d)) < 0)
    return r;

  if (!d) {
    if (discarding > 0) {
      r = discarding;

      if (args[0].type == COMMAND_ARG_FORMULA) {
	if ((r = formula_proc(args[0].arg.formula.element, &value)) < 0)
	  return r;
	if ((r = value_is_true(value)) < 0)
	  return r;
      }

      discarding = r ? 0 : 1;
    } else {
      discarding = -1;
    }

    if (!discarding) {
      if (args[1].type == COMMAND_ARG_FORMULA) {
	if ((r = formula_proc(args[1].arg.formula.element, NULL)) < 0)
	  return r;
      }
    } else {
      if (args[2].type == COMMAND_ARG_FORMULA) {
	if ((r = formula_proc(args[2].arg.formula.element, NULL)) < 0)
	  return r;
      }
    }
  }

  return 0;
}

static int proc_endif(command_arg_t args)
{
  int r = 0;

  if ((r = position_pop(POSITION_TYPE_IF, NULL, NULL, NULL, &discarding)) < 0)
    return r;

  return 0;
}

static int proc_loop(spot_t spot, command_arg_t args)
{
  int r;
  value_t value, v;

  if (args[1].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[1].arg.formula.element, NULL)) < 0)
      return r;
  }

  if (args[2].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[2].arg.formula.element, &v)) < 0)
      return r;
  }

  if (args[3].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[3].arg.formula.element, NULL)) < 0)
      return r;
  }

  if (args[0].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[0].arg.formula.element, &value)) < 0)
      return r;
    if (args[2].type == COMMAND_ARG_FORMULA) {
      if (value) {
	if ((r = value_copy_values(&value, v)) < 0)
	  return r;
      }
    } else {
      if (value) {
	if ((r = value_set_integer(value, 0)) < 0)
	  return r;
      }
    }
  }

  if ((r = position_push(POSITION_TYPE_LOOP, spot, NULL, 0)) < 0)
    return r;

  return 0;
}

static int proc_for(spot_t spot, command_arg_t args)
{
  int r;

  if (args[0].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[0].arg.formula.element, NULL)) < 0)
      return r;
  }

  if ((r = position_push(POSITION_TYPE_FOR, spot, NULL, 0)) < 0)
    return r;

  return 0;
}

static int proc_loopend(spot_t spot, command_arg_t args)
{
  int r;
  integer_t integer = 0, count = -1, start = 0, step = 1, end = 1;
  struct spot s;
  value_t value = NULL;

  if ((r = position_pop(POSITION_TYPE_LOOP, NULL, &s, NULL, NULL)) < 0)
    return r;

  if (s.line == NULL)
    return NLL_ERRCODE_NLL_NO_CONTINUE_POINT;

  if (args[0].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[0].arg.formula.element, &value)) < 0)
      return r;
  }

  if (args[1].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[1].arg.formula.element, NULL)) < 0)
      return r;
  }

  args = s.command->args;

  if (value) {
    if ((args[0].type != COMMAND_ARG_FORMULA) ||
	!value_is_same(value, formula_get_value(args[0].arg.formula.element)))
      return NLL_ERRCODE_POSITION_DIFFERENT_VARIABLE;
  }

  value = NULL;
  if (args[0].type == COMMAND_ARG_FORMULA)
    value = formula_get_value(args[0].arg.formula.element);

  if (args[1].type == COMMAND_ARG_FORMULA) {
    if ((r = value_get_integer(formula_get_value(args[1].arg.formula.element), &count)) < 0)
      return r;
  }
  if (args[2].type == COMMAND_ARG_FORMULA) {
    if ((r = value_get_integer(formula_get_value(args[2].arg.formula.element), &start)) < 0)
      return r;
  }
  if (args[3].type == COMMAND_ARG_FORMULA) {
    if ((r = value_get_integer(formula_get_value(args[3].arg.formula.element), &step)) < 0)
      return r;
  }

  if (value) {
    if ((r = value_get_integer(value, &integer)) < 0)
      return r;

    integer += step;

    if ((r = value_set_integer(value, integer)) < 0)
      return r;

    end = start + step * count;
  }

  if ((count < 0) || (integer != end)) {
    if ((r = position_push(POSITION_TYPE_LOOP, &s, NULL, 0)) < 0)
      return r;
    spot->line    = s.line;
    spot->command = s.command;
    return 1;
  }

  return 0;
}

static int proc_next(spot_t spot, command_arg_t args)
{
  int r;
  struct spot s;
  value_t value = NULL;

  if ((r = position_pop(POSITION_TYPE_FOR, NULL, &s, NULL, NULL)) < 0)
    return r;

  if (s.line == NULL)
    return NLL_ERRCODE_NLL_NO_CONTINUE_POINT;

  if (args[0].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[0].arg.formula.element, NULL)) < 0)
      return r;
  }

  args = s.command->args;

  if (args[2].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[2].arg.formula.element, NULL)) < 0)
      return r;
  }

  if (args[1].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[1].arg.formula.element, &value)) < 0)
      return r;
  }

  if (value) {
    if ((r = value_is_true(value)) < 0)
      return r;
    if (r)
      value = NULL;
  }

  if (!value) {
    if ((r = position_push(POSITION_TYPE_FOR, &s, NULL, 0)) < 0)
      return r;
    spot->line    = s.line;
    spot->command = s.command;
    return 1;
  }

  return 0;
}

static int proc_discard(command_arg_t args)
{
  int r;
  integer_t num = 1;
  value_t value;

  if (args[0].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[0].arg.formula.element, &value)) < 0)
      return r;
    if (value) {
      if ((r = value_get_integer(value, &num)) < 0)
	return r;
    }
  }

  if ((r = _discard(num)) < 0)
    return r;

  return 0;
}

static int proc_dim(command_arg_t args)
{
#define ARRAY_DIM_MAXNUM (COMMAND_ARG_MAXNUM - 1)
  int r, i, n[ARRAY_DIM_MAXNUM + 1];
  integer_t integer;
  value_t value;

  for (i = 0; i < ARRAY_DIM_MAXNUM; i++) {
    n[i] = -1;
    if (args[i + 1].type == COMMAND_ARG_FORMULA) {
      if ((r = formula_proc(args[i + 1].arg.formula.element, &value)) < 0)
	return r;
      if (value) {
	if ((r = value_get_integer(value, &integer)) < 0)
	  return r;
	n[i] = integer;
      } else {
	n[i] = 1;
      }
    }
  }
  n[i] = -1;

  if (args[0].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[0].arg.formula.element, &value)) < 0)
      return r;
    if (value) {
      if ((r = array_make(value, n)) < 0)
	return r;
    }
  }

  return 0;
}

static int proc_memory(command_arg_t args)
{
  int r;
  integer_t integer;
  value_t value, v;
  area_t area = NULL;

  if (args[0].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[0].arg.formula.element, &value)) < 0)
      goto err;
  }

  if (args[1].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[1].arg.formula.element, &v)) < 0)
      goto err;
    if (args[0].type == COMMAND_ARG_FORMULA) {
      if ((r = value_get_integer(v, &integer)) < 0)
	goto err;
      if ((r = area_alloc(&area, integer)) < 0)
	goto err;
    }
  }

  if ((r = value_set_area(value, area)) < 0)
    goto err;
  area = NULL;

  return 0;

err:
  if (area) {
    area->refcount++;
    area_free(area);
  }
  return r;
}

static int proc_push(command_arg_t args)
{
  int r, i;
  value_t value;

  for (i = 0; i < COMMAND_ARG_MAXNUM; i++) {
    if (args[i].type == COMMAND_ARG_FORMULA) {
      if ((r = formula_proc(args[i].arg.formula.element, &value)) < 0)
	return r;
      for (; value; value = value->next) {
	if ((r = stack_push(value)) < 0)
	  return r;
      }
    }
  }

  return 0;
}

static int proc_pop(command_arg_t args)
{
  int r, i, n = 0;
  value_t value;

  for (i = 0; i < COMMAND_ARG_MAXNUM; i++) {
    if (args[i].type == COMMAND_ARG_FORMULA) {
      if ((r = formula_proc(args[i].arg.formula.element, &value)) < 0)
	return r;
      for (; value; value = value->next) {
	if ((r = stack_pop(value)) < 0)
	  return r;
      }
      n++;
    }
  }

  if (!n) {
    if ((r = stack_pop(NULL)) < 0)
      return r;
  }

  return 0;
}

static int proc_dfrom(command_arg_t args)
{
  int r;
  value_t value;
  label_t label = NULL;

  if (args[0].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[0].arg.formula.element, &value)) < 0)
      return r;
    if (value) {
      if ((r = value_get_label(value, &label)) < 0)
	return r;
    }
  }

  if (!label) {
    init_dread();
  } else {
    if (!label->spot.line)
      return NLL_ERRCODE_LABEL_NOT_FOUND;
    dread_spot.spot.line    = label->spot.line;
    dread_spot.spot.command = label->spot.command;
    dread_spot.index = 0;
  }

  return 0;
}

static int dread(value_t value)
{
  int r, i;
  value_t v;

  while (dread_spot.spot.line) {
    if (!dread_spot.spot.command) {
      dread_spot.spot.line = dread_spot.spot.line->next;
      dread_spot.spot.command = dread_spot.spot.line ? dread_spot.spot.line->commands : NULL;
      dread_spot.index = 0;
      continue;
    } else if (dread_spot.spot.command->ope->type == COMMAND_DATA) {
      if (dread_spot.index < COMMAND_ARG_MAXNUM) {
	i = dread_spot.index++;
	if (dread_spot.spot.command->args[i].type == COMMAND_ARG_FORMULA) {
	  if ((r = formula_proc(dread_spot.spot.command->args[i].arg.formula.element, &v)) < 0)
	    return r;
	  break;
	}
	continue;
      }
    }
    dread_spot.spot.command = dread_spot.spot.command->next;
    dread_spot.index = 0;
  }

  if (!dread_spot.spot.line)
    return NLL_ERRCODE_NLL_NO_DATA;

  if (value) {
    if ((r = value_copy_values(&value, v)) < 0)
      return r;
  }

  return 0;
}

static int proc_dread(command_arg_t args)
{
  int r, i;
  value_t value;

  for (i = 0; i < COMMAND_ARG_MAXNUM; i++) {
    if (args[i].type == COMMAND_ARG_FORMULA) {
      if ((r = formula_proc(args[i].arg.formula.element, &value)) < 0)
	return r;
      if ((r = dread(value)) < 0)
	return r;
    }
  }

  return 0;
}

static int proc_run(spot_t spot, command_arg_t args)
{
  int r, n;
  integer_t integer = 1;
  value_t value, argv;
  line_t line = NULL;

  if (args[0].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[0].arg.formula.element, &value)) < 0)
      return r;
    if (value) {
      if ((r = value_get_integer(value, &integer)) < 0)
	return r;
    }
  }

  if ((r = line_get(integer, &line)) < 0)
    return r;

  if (args[1].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[1].arg.formula.element, &argv)) < 0)
      return r;
    if (value_get_type(argv) == VALUE_TYPE_VECTOR) {
      if ((r = value_get_vector(argv, &argv)) < 0)
	return r;
    }
    for (n = 0, value = argv; value; n++, value = value->next)
      ;
    nll_make_argc(n);
    nll_make_argv(n, NULL);
    for (n = 0, value = argv; value; n++, value = value->next)
      nll_set_arg(n, value);
  }

  clear_continue(NULL);
  init_dread();
  position_clean();
  stack_clean();
  line_clean();
  variable_clean();
  stepping = 0;
  skipping = 0;
  discarding = 0;

  if (line) {
    spot->line    = line;
    spot->command = line->commands;
    return 1;
  }

  return 0;
}

static int proc_stop(command_arg_t args)
{
  int r, i;
  value_t value;

  for (i = 0; i < COMMAND_ARG_MAXNUM; i++) {
    if (args[i].type == COMMAND_ARG_FORMULA) {
      if ((r = formula_proc(args[i].arg.formula.element, &value)) < 0)
	goto ret;
      if (value) {
	value_output_values(nll_stdout, value, " ");
	nll_wait_output(nll_stdout);
	fputs("\n", nll_stdout);
      }
    }
  }

  r = 0;

ret:
  fflush(nll_stdout);

  return r;
}

static int proc_break(command_arg_t args)
{
  int r;
  integer_t integer0 = 0, integer1 = 0;
  value_t value;
  line_t line = NULL;
  command_t command;

  if (args[0].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[0].arg.formula.element, &value)) < 0)
      return r;
    if (value) {
      if ((r = value_get_integer(value, &integer0)) < 0)
	return r;
    }
  }

  if (args[1].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[1].arg.formula.element, &value)) < 0)
      return r;
    if (value) {
      if ((r = value_get_integer(value, &integer1)) < 0)
	return r;
    }
  }

  if ((r = line_get(integer0, &line)) < 0)
    return r;

  if (!line) {
    clear_break(NULL);
  } else {
    for (command = line->commands; command && (integer1 > 0); integer1--) {
      command = command->next;
    }
    break_spot.line    = line;
    break_spot.command = command;
  }

  return 0;
}

static int proc_step(command_arg_t args)
{
  int r;
  integer_t integer = 1;
  value_t value;

  if (args[0].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[0].arg.formula.element, &value)) < 0)
      return r;
    if (value) {
      if ((r = value_get_integer(value, &integer)) < 0)
	return r;
    }
  }

  stepping = integer;

  return 0;
}

static int proc_skip(command_arg_t args)
{
  int r;
  integer_t integer = 1;
  value_t value;

  if (args[0].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[0].arg.formula.element, &value)) < 0)
      return r;
    if (value) {
      if ((r = value_get_integer(value, &integer)) < 0)
	return r;
    }
  }

  skipping = integer;

  return 0;
}

static int proc_new(spot_t spot, command_arg_t args)
{
  int r, i, retval = 1;
  value_t value;
  variable_t variable;

  for (i = 0; i < COMMAND_ARG_MAXNUM; i++) {
    if (args[i].type == COMMAND_ARG_FORMULA) {
      variable = formula_get_variable(args[i].arg.formula.element);
      if (variable) {
	if ((r = variable_new(variable)) < 0)
	  return r;
      } else {
	if ((r = formula_proc(args[i].arg.formula.element, &value)) < 0)
	  return r;
	if ((r = value_clear(value_entity(value))) < 0)
	  return r;
      }
      retval = 0;
    }
  }

  return retval;
}

static int proc_list(command_arg_t args)
{
  int r;
  integer_t integer0 = -1, integer1 = -1;
  value_t value;

  if (args[0].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[0].arg.formula.element, &value)) < 0)
      return r;
    if (value) {
      if ((r = value_get_integer(value, &integer0)) < 0)
	return r;
    }
  }

  integer1 = integer0;

  if (args[1].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[1].arg.formula.element, &value)) < 0)
      return r;
    if (value) {
      if ((r = value_get_integer(value, &integer1)) < 0)
	return r;
    }
  }

  line_print(nll_stdout, integer0, integer1);
  fflush(nll_stdout);

  return 0;
}

static int proc_save(command_arg_t args)
{
  int r;
  char *string = "sample.nll", *s;
  integer_t integer0 = -1, integer1 = -1;
  value_t value;
  FILE *fp;

  if (nll_is_nosystem())
    return NLL_ERRCODE_NLL_INVALID_COMMAND;

  if (args[0].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[0].arg.formula.element, &value)) < 0)
      return r;
    if (value) {
      if ((r = value_get_string(value, &s, NULL)) < 0)
	return r;
      if (s)
	string = s;
    }
  }

  if (args[1].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[1].arg.formula.element, &value)) < 0)
      return r;
    if (value) {
      if ((r = value_get_integer(value, &integer0)) < 0)
	return r;
    }
  }

  integer1 = integer0;

  if (args[2].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[2].arg.formula.element, &value)) < 0)
      return r;
    if (value) {
      if ((r = value_get_integer(value, &integer1)) < 0)
	return r;
    }
  }

  fp = fopen(string, "w");
  if (!fp)
    return NLL_ERRCODE_FILE_NOT_FOUND;

  line_print(fp, integer0, integer1);

  fclose(fp);

  return 0;
}

static int proc_load(command_arg_t args)
{
  int r;
  char *string = "sample.nll", *s;
  value_t value;

  if (nll_is_nosystem())
    return NLL_ERRCODE_NLL_INVALID_COMMAND;

  if (args[0].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[0].arg.formula.element, &value)) < 0)
      return r;
    if (value) {
      if ((r = value_get_string(value, &s, NULL)) < 0)
	return r;
      if (s)
	string = s;
    }
  }

  if ((r = nll_file_open(string)) < 0)
    return r;

  return 0;
}

static int proc_stdin(command_arg_t args)
{
  int r;
  char *filename = NULL;
  value_t value;
  FILE *fp;

  if (nll_is_nosystem())
    return NLL_ERRCODE_NLL_INVALID_COMMAND;

  if (args[0].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[0].arg.formula.element, &value)) < 0)
      return r;
    if (value) {
      if ((r = value_get_string(value, &filename, NULL)) < 0)
	return r;
    }
  }

  if (!filename) {
    if ((r = nll_stdin_restore()) < 0)
      return r;
  } else {
    fp = fopen(filename, "r");
    if (!fp)
      return NLL_ERRCODE_FILE_NOT_FOUND;
    if ((r = nll_stdin_save(fp)) < 0)
      return r;
  }

  return 0;
}

static int proc_stdout(command_arg_t args)
{
  int r;
  char *filename = NULL;
  value_t value;
  FILE *fp;

  if (nll_is_nosystem())
    return NLL_ERRCODE_NLL_INVALID_COMMAND;

  if (args[0].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[0].arg.formula.element, &value)) < 0)
      return r;
    if (value) {
      if ((r = value_get_string(value, &filename, NULL)) < 0)
	return r;
    }
  }

  if (!filename) {
    if ((r = nll_stdout_restore()) < 0)
      return r;
  } else {
    fp = fopen(filename, "w");
    if (!fp)
      return NLL_ERRCODE_FILE_NOT_FOUND;
    if ((r = nll_stdout_save(fp)) < 0)
      return r;
  }

  return 0;
}

static int proc_eval(spot_t spot, command_arg_t args)
{
  int r;
  char *string = NULL;
  value_t value = NULL, v;

  if (args[0].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[0].arg.formula.element, &v)) < 0)
      return r;
    if (v) {
      if ((r = value_get_string(v, &string, NULL)) < 0)
	return r;
    }
  }

  if (args[1].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[1].arg.formula.element, &value)) < 0)
      return r;
  }

  if (!string)
    return NLL_ERRCODE_VALUE_INVALID_TYPE;

  return nll_parse_exec(spot, POSITION_TYPE_EVAL, string, value, NULL);
}

static int proc_evalf(spot_t spot, command_arg_t args)
{
  int r, retcode;
  char *filename = NULL;
  value_t value = NULL, v;
  FILE *fp = NULL;
  char buffer[LINE_MAXLEN + 1];

  if (args[0].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[0].arg.formula.element, &v)) < 0)
      return r;
    if (v) {
      if ((r = value_get_string(v, &filename, NULL)) < 0)
	return r;
    }
  }

  if (args[1].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[1].arg.formula.element, &value)) < 0)
      return r;
  }

  if (!filename)
    return NLL_ERRCODE_VALUE_INVALID_TYPE;

  fp = fopen(filename, "r");
  if (!fp)
    return NLL_ERRCODE_FILE_NOT_FOUND;

  while (nll_fgets(buffer, LINE_MAXLEN, fp)) {
    if ((r = nll_parse_exec(spot, POSITION_TYPE_EVAL, buffer, value, &retcode)) < 0)
      goto err;
    if (retcode)
      break;
  }

  fclose(fp);

  return 0;

err:
  if (fp)
    fclose(fp);
  return r;
}

static int proc_edit(command_arg_t args)
{
  int r;
  integer_t integer = 0;
  value_t value;

  if (args[0].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[0].arg.formula.element, &value)) < 0)
      return r;
    if (value) {
      if ((r = value_get_integer(value, &integer)) < 0)
	return r;
    }
  }

  if ((r = nll_edit_set(integer)) < 0)
    return r;

  return 0;
}

static int proc_wait(command_arg_t args)
{
  int r;
  integer_t integer = 100, integer2 = 0, integer3 = 0, integer4 = 0;
  value_t value;

  if (args[1].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[1].arg.formula.element, &value)) < 0)
      return r;
    if (value) {
      if ((r = value_get_integer(value, &integer2)) < 0)
	return r;
      integer = 0;
    }
  }

  if (args[0].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[0].arg.formula.element, &value)) < 0)
      return r;
    if (value) {
      if ((r = value_get_integer(value, &integer)) < 0)
	return r;
    }
  }

  if (args[2].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[2].arg.formula.element, &value)) < 0)
      return r;
    if (value) {
      if ((r = value_get_integer(value, &integer3)) < 0)
	return r;
    }
  }

  if (args[3].type == COMMAND_ARG_FORMULA) {
    if ((r = formula_proc(args[3].arg.formula.element, &value)) < 0)
      return r;
    if (value) {
      if ((r = value_get_integer(value, &integer4)) < 0)
	return r;
    }
  }

  nll_usleep(integer * 10000 + integer2 * 100 + integer3);

  if (integer4)
    nll_sleep(integer4);

  return 0;
}

static int proc_dump(command_arg_t args)
{
  int r, i, all = 1;
  integer_t flags;
  value_t value;

  for (i = 0; i < ARRAY_DIM_MAXNUM + 1; i++) {
    if (i == ARRAY_DIM_MAXNUM) {
      if (!all)
	break;
      flags = 0;
    } else {
      if (args[i].type != COMMAND_ARG_FORMULA)
	continue;
      if ((r = formula_proc(args[i].arg.formula.element, &value)) < 0)
	return r;
      if (!value)
	continue;
      if ((r = value_get_integer(value, &flags)) < 0)
	return r;
    }

    if (!flags)
      flags = NLL_D_ALL;

    if (flags & NLL_D_VARIABLE) variable_dump(nll_stdout, NULL);
    if (flags & NLL_D_STRING  ) string_dump(nll_stdout, NULL);
    if (flags & NLL_D_LABEL   ) label_dump(nll_stdout, NULL);
    if (flags & NLL_D_LINE    ) line_dump(nll_stdout, NULL);
    if (flags & NLL_D_STACK   ) stack_dump(nll_stdout);
    if (flags & NLL_D_POSITION) position_dump(nll_stdout);
    if (flags & NLL_L_COMMAND ) command_list(nll_stdout);
    if (flags & NLL_L_FUNCTION) function_list(nll_stdout);
    if (flags & NLL_L_CONST   ) variable_const_list(nll_stdout);
    if (flags & NLL_L_OPERATOR) formula_operator_list(nll_stdout);
    fflush(nll_stdout);
    all = 0;
  }

  return 0;
}

static void set_errcode(command_t command, int r)
{
  if (command)
    command->errcode = r;
}

#define ERRBREAK(r, command, param) \
	do { NLL_ERRPRINT((r), (param)); set_errcode((command), (r)); goto brk; } while (0)

int nll_parse(line_t line)
{
  char *p, *buffer, *pc;
  char name[SYMBOL_NAMELEN + 1];
  int r, i, count = 0;
  command_t command = NULL, *tailp;
  struct spot s;

  buffer = line->buffer;
  p = buffer;

  tailp = &line->commands;

  while (*p) {
    if (isspace(*p)) {
      p++;
      continue;
    }

    pc = p;

    if ((r = command_alloc(&command)) < 0)
      ERRBREAK(r, command, pc);
    command->line = pc;

    if ((r = command_get(COMMAND_NONE, &command->ope)) < 0)
      ERRBREAK(r, command, pc);

    if (*p == '.') {
      p++;
      while (isspace(*p))
	p++;
      if ((r = formula_get_symbol(name, p)) < 0)
	ERRBREAK(r, command, pc);
      if (r == 0)
	ERRBREAK(NLL_ERRCODE_LABEL_INVALID_NAME, command, pc);
      memcpy(p, name, r);
      p += r;

      s.line    = line;
      s.command = command;
      if ((r = command_set_label(command, name, &s)) < 0)
	ERRBREAK(r, command, pc);

      if ((r = formula_parse(p, &p, &command->label.args, ";:")) < 0)
	ERRBREAK(r, command, pc);
      if (command->label.args) {
	if ((r = formula_set_raw(command->label.args)) < 0)
	  ERRBREAK(r, command, pc);
      }

      if (*p == ':')
	p++;
    }

    if ((r = command_search(p, &p, &command->ope)) < 0) {
      if ((r = formula_parse(p, &p, &command->prefix, ";:")) < 0)
	ERRBREAK(r, command, pc);
      if (*p == ':') {
	p++;
	while (isspace(*p))
	  p++;
	if ((r = command_search(p, &p, &command->ope)) < 0) {
	  if ((r = command_get(COMMAND_ARGS, &command->ope)) < 0)
	    ERRBREAK(r, command, pc);
	}
      }
    }

    while (isspace(*p))
      p++;

    switch (command->ope->type) {
    case COMMAND_NONE:
      break;

    case COMMAND_COMMENT:
      goto brk;

    case COMMAND_CONTINUE:
      break;

    case COMMAND_END:
    case COMMAND_EXIT:
    case COMMAND_ARGS:
    case COMMAND_LET:
    case COMMAND_PRINT:
    case COMMAND_GOTO:
    case COMMAND_GONEXT:
    case COMMAND_GOSUB:
    case COMMAND_RETURN:
    case COMMAND_IF:
    case COMMAND_ELSE:
    case COMMAND_ENDIF:
    case COMMAND_LOOP:
    case COMMAND_LOOPEND:
    case COMMAND_FOR:
    case COMMAND_NEXT:
    case COMMAND_DISCARD:
    case COMMAND_DIM:
    case COMMAND_MEMORY:
    case COMMAND_PUSH:
    case COMMAND_POP:
    case COMMAND_DFROM:
    case COMMAND_DREAD:
    case COMMAND_DATA:
    case COMMAND_RUN:
    case COMMAND_STOP:
    case COMMAND_SKIP:
    case COMMAND_BREAK:
    case COMMAND_STEP:
    case COMMAND_NEW:
    case COMMAND_LIST:
    case COMMAND_SAVE:
    case COMMAND_LOAD:
    case COMMAND_STDIN:
    case COMMAND_STDOUT:
    case COMMAND_EVAL:
    case COMMAND_EVALF:
    case COMMAND_EDIT:
    case COMMAND_WAIT:
    case COMMAND_DUMP:
      for (i = 0; i < COMMAND_ARG_MAXNUM; i++) {
	if ((r = formula_parse(p, &p, &command->args[i].arg.formula.element, NULL)) < 0)
	  ERRBREAK(r, command, pc);
	if (command->args[i].arg.formula.element)
	  command->args[i].type = COMMAND_ARG_FORMULA;
	if (*p == ',') p++; else break;
      }
      if (i == COMMAND_ARG_MAXNUM)
	ERRBREAK(NLL_ERRCODE_COMMAND_MUCH_ARGS, command, pc);
      break;

    case COMMAND_UNKNOWN:
    default:
      ERRBREAK(NLL_ERRCODE_COMMAND_NOT_FOUND, command, pc);
    }

    switch (command->ope->type) {
    case COMMAND_GOTO:
    case COMMAND_GOSUB:
      if (command->args[1].type == COMMAND_ARG_FORMULA) {
	if ((r = formula_set_raw(command->args[1].arg.formula.element)) < 0)
	  return r;
      }
      break;
    default:
      break;
    }

    if (*p) {
      if (*p != ';')
	ERRBREAK(NLL_ERRCODE_COMMAND_INVALID_FORMAT, command, pc);
      p++;
    }

    command->next = *tailp;
    *tailp = command;
    tailp = &(command->next);
    command = NULL;
    count++;
  }

  r = count;

brk:
  if (command) {
    if (!command->errcode) {
      command_free(command);
    } else {
      command_clear(command);
      command->next = *tailp;
      *tailp = command;
      tailp = &(command->next);
    }
  }

  return r;
}

static int nll_proc(spot_t spot, int *retcodep)
{
  line_t line;
  command_t command;
  int r;
  struct spot s;
  value_t value;

  line    = spot->line;
  command = spot->command;

  for (; command; command = command->next) {
    if (discarding) {
      if (command->errcode)
	continue;
      if (command->prefix)
	continue;
      switch (command->ope->type) {
      case COMMAND_IF:
	if ((r = proc_if(command->args)) < 0)
	  ERRBREAK(r, NULL, command->line);
	break;
      case COMMAND_ELSE:
	if ((r = proc_else(command->args)) < 0)
	  ERRBREAK(r, NULL, command->line);
	break;
      case COMMAND_ENDIF:
	if ((r = proc_endif(command->args)) < 0)
	  ERRBREAK(r, NULL, command->line);
	break;
      default:
	break;
      }
      continue;
    }

    if (skipping && (line_is_exist(line) > 0)) {
      skipping--;
      continue;
    }

    if (nll_is_finished()) {
      fprintf(nll_stdout, "\nBreak at: %s\n", command->line);
      goto brk;
    }

    if (!nll_finish_is_locked()) {
      if (stepping && (line_is_exist(line) > 0)) {
	stepping--;
	if (!stepping) {
	  fprintf(nll_stdout, "\nStep break at: %s\n", command->line);
	  goto brk;
	}
      }
    }

    if (break_spot.line && (line_is_exist(line) > 0)) {
      if ((line == break_spot.line) && (command == break_spot.command)) {
	clear_break(line);
	fprintf(nll_stdout, "\nBreakpoint at: %s\n", command->line);
	goto brk;
      }
    }

    if (command->errcode)
      ERRBREAK(command->errcode, NULL, command->line);

    if (command->label.args) {
      if ((r = formula_proc(command->label.args, &value)) < 0)
	ERRBREAK(r, NULL, command->line);
      if ((r = stack_pop_type(STACK_TYPE_CALL, value)) < 0)
	ERRBREAK(r, NULL, command->line);
    }

    if (command->prefix) {
      if ((r = formula_proc(command->prefix, &value)) < 0)
	ERRBREAK(r, NULL, command->line);
      if (command->ope->type == COMMAND_NONE)
	continue;
      if (!value) {
	r = 0;
      } else {
	if ((r = value_is_true(value)) < 0)
	  ERRBREAK(r, NULL, command->line);
      }
      if (!r)
	continue;
    }

    switch (command->ope->type) {
    case COMMAND_END:
      if ((r = proc_end(command->args, retcodep)) < 0)
	ERRBREAK(r, NULL, command->line);
      goto end;

    case COMMAND_EXIT:
      if ((r = proc_end(command->args, retcodep)) < 0)
	ERRBREAK(r, NULL, command->line);
      goto ext;

    case COMMAND_ARGS:
      if ((r = proc_args(command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      break;

    case COMMAND_LET:
      if ((r = proc_let(command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      break;

    case COMMAND_PRINT:
      if ((r = proc_print(command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      break;

    case COMMAND_GOTO:
      if ((r = proc_goto(spot, command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      goto next;

    case COMMAND_GONEXT:
      s.line    = line;
      s.command = command;
      if ((r = proc_gonext(spot, &s, line, command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      goto next;

    case COMMAND_GOSUB:
      s.line    = line;
      s.command = command;
      if ((r = proc_gosub(spot, &s, command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      goto next;

    case COMMAND_RETURN:
      if ((r = proc_return(spot, command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      if (!r) {
	if (retcodep)
	  *retcodep = 1;
	goto end;
      }
      spot->command = spot->command->next;
      goto next;

    case COMMAND_IF:
      if ((r = proc_if(command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      break;

    case COMMAND_ELSE:
      if ((r = proc_else(command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      break;

    case COMMAND_ENDIF:
      if ((r = proc_endif(command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      break;

    case COMMAND_LOOP:
      s.line    = line;
      s.command = command;
      if ((r = proc_loop(&s, command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      break;

    case COMMAND_FOR:
      s.line    = line;
      s.command = command;
      if ((r = proc_for(&s, command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      break;

    case COMMAND_LOOPEND:
      if ((r = proc_loopend(spot, command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      if (!r)
	break;
      spot->command = spot->command->next;
      goto next;

    case COMMAND_NEXT:
      if ((r = proc_next(spot, command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      if (!r)
	break;
      spot->command = spot->command->next;
      goto next;

    case COMMAND_DISCARD:
      if ((r = proc_discard(command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      break;

    case COMMAND_DIM:
      if ((r = proc_dim(command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      break;

    case COMMAND_MEMORY:
      if ((r = proc_memory(command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      break;

    case COMMAND_PUSH:
      if ((r = proc_push(command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      break;

    case COMMAND_POP:
      if ((r = proc_pop(command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      break;

    case COMMAND_DFROM:
      if ((r = proc_dfrom(command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      break;

    case COMMAND_DREAD:
      if ((r = proc_dread(command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      break;

    case COMMAND_RUN:
      if ((r = proc_run(spot, command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      if (!r)
	break;
      goto next;

    case COMMAND_STOP:
      if ((r = proc_stop(command->args)) < 0)
	; /* no break at error */
      nll_finish();
      break;

    case COMMAND_BREAK:
      if ((r = proc_break(command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      break;

    case COMMAND_STEP:
      if ((r = proc_step(command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      if (stepping) stepping++;
      goto cont;

    case COMMAND_CONTINUE:
      goto cont;

    case COMMAND_SKIP:
      if ((r = proc_skip(command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      break;

    case COMMAND_NEW:
      if ((r = proc_new(spot, command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      if (!r)
	break;
      goto init;

    case COMMAND_LIST:
      if ((r = proc_list(command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      break;

    case COMMAND_SAVE:
      if ((r = proc_save(command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      break;

    case COMMAND_LOAD:
      if ((r = proc_load(command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      break;

    case COMMAND_STDIN:
      if ((r = proc_stdin(command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      break;

    case COMMAND_STDOUT:
      if ((r = proc_stdout(command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      break;

    case COMMAND_EVAL:
      if ((r = proc_eval(spot, command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      break;

    case COMMAND_EVALF:
      if ((r = proc_evalf(spot, command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      break;

    case COMMAND_EDIT:
      if ((r = proc_edit(command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      break;

    case COMMAND_WAIT:
      if ((r = proc_wait(command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      break;

    case COMMAND_DUMP:
      if ((r = proc_dump(command->args)) < 0)
	ERRBREAK(r, NULL, command->line);
      break;

    case COMMAND_NONE:
    case COMMAND_UNKNOWN:
    case COMMAND_DATA:
    default:
      break;
    }
  }

  spot->line = line->next;
  if (spot->line)
    spot->command = spot->line->commands;

next:
  return 0;

cont:
  if ((r = get_continue(spot)) < 0)
    ERRBREAK(r, NULL, command->line);
  clear_continue(NULL);
  return 0;

end:
  spot->line = NULL;
  return 0;

init:
  nll_initialize_set();
  spot->line = NULL;
  return 1;

brk:
  if (retcodep)
    *retcodep = 1;
  if (nll_finish_is_locked())
    goto ext;
  s.line    = line;
  s.command = command;
  set_continue(&s);
  spot->line = NULL;
  return 0;

ext:
  spot->line = NULL;
  return -1;
}

static int nll_run(spot_t spot, int *retcodep)
{
  int r = 0;

  if (retcodep)
    *retcodep = 0;

  while (spot->line) {
    r = nll_proc(spot, retcodep);
    fflush(nll_stdout);
  }

  return r;
}

int nll_start(line_t line, command_t command, int *retcodep)
{
  struct spot spot;
  int r;

  nll_finished_clear();

  spot.line = line;

  if (!line)
    spot.command = NULL;
  else
    spot.command = command ? command : line->commands;

#ifdef USE_FRAMEBUF
  framebuf_enable(1);
#endif

  r = nll_run(&spot, retcodep);

#ifdef USE_FRAMEBUF
  framebuf_enable(0);
#endif

  return r;
}

int nll_exec(spot_t spot, spot_t current, position_type_t type, value_t value, int *retcodep)
{
  int r, errcode = 0, locked;

  if ((r = position_push(type, current, NULL, 0)) < 0)
    return r;

  locked = nll_finish_lock(1);
  if (nll_run(spot, retcodep))
    errcode = NLL_ERRCODE_NLL_INVALID_COMMAND;
  nll_finish_lock(locked);

  if ((r = position_pop(type, NULL, NULL, value, NULL)) < 0)
    return r;

  return errcode;
}

int nll_parse_exec(spot_t current, position_type_t type, char *buffer, value_t value, int *retcodep)
{
  int r;
  line_t line;
  struct spot spot;

  if ((r = line_set(-1, -1, buffer, &line, 0, 0)) < 0)
    return r;
  if (!line)
    return NLL_ERRCODE_VALUE_INVALID_TYPE;

  if ((r = nll_parse(line)) < 0)
    goto err;

  /* dummy return value for the return from the end of lines */
  if ((r = stack_push_type(STACK_TYPE_CALL, NULL)) < 0)
    return r;

  spot.line = line;
  spot.command = line->commands;

  if ((r = nll_exec(&spot, current, type, NULL, retcodep)) < 0)
    goto err;

  if ((r = stack_pop_type(STACK_TYPE_CALL, value)) < 0)
    return r;

  if ((r = line_free(line)) < 0)
    return r;

  return 0;

err:
  if (line)
    line_free(line);
  return r;
}

char *nll_line_buffer(line_t line)
{
  return line->buffer;
}

int nll_line_edit(int number, int number2, char *buffer, line_t *linep, int save, int move)
{
  return line_set(number, number2, buffer, linep, save, move);
}

int nll_line_free(line_t line)
{
  return line_free(line);
}
