/* Printer status information. */
/* Controls communication between program and printer */
/* Created 07.11.2004 T. Milius
   Changed 12.09.2005 T. Milius Added handling of variable INTERFACE
                                Added special protocols for parallel port
   Changed 26.09.2005 T. Milius Interface dependent definitions/actions in
                                printeractions
   Changed 26.09.2005 T. Milius printer switch action added
   Changed 17.01.2007 T. Milius OS_SubstituteArgs32 back to OS_SubstituteArgs because of backward compitibility */
/* (c) Copyright 2004-2007 by Thomas Milius Stade, Germany
   Source must not be altered without agreement of the owner.
   The owner of the source is allowed to use this code inside programs without
   publishing the code of this programs. These programs may be commercial.
   Other developers can use this source freely inside own software if the source
   code of this programs is made public to same conditions like valid to this code
   and no commercial profit is taken from the programs based on this code

   Code or parts of it are not allowed to be used within GPL code or
   similar licenses which are "infecting" other code and trying to "supersede"
   other licenses. */
/* RISC OS */

#ifndef communication_h
#define communication_h

/* !!!!!!!!!! libraries !!!!!!!!!! */
/* ---------- ANSI-C ---------- */
#include <ctype.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

/* ---------- RISC OS ---------- */
#include <kernel.h>
#include <swis.h>

/* ------------ own ------------ */
#include "common.h"
#include "draw.h"
#include "variables.h"
#include "printeractions.h"

/* !!!!!!!!!!! definitions !!!!!!!!!! */
/* Actions */
#define ACTION_TAG_GET_CARTRIDGE_INFO "GetCartridgeInfo:"
#define ACTION_TAG_CHANGE_CARTRIDGE   "ChangeCartridge: "
#define ACTION_TAG_CLEAN_HEAD         "CleanHead: "
#define ACTION_TAG_GET_STATUS         "GetStatus: "
#define ACTION_TAG_RESET_PRINTER      "ResetPrinter: "
#define ACTION_TAG_CALIBRATE_HEAD     "CalibrateHead: "
#define ACTION_TAG_PRINT_TESTPATTERN  "PrintTestpattern:"
#define ACTION_TAG_CHANGE_HEAD        "ChangeHead: "
#define ACTION_TAG_SWITCH_PRINTER     "SwitchPrinter:"
#define ACTION_TAG_INIT               "Init:"
#define ACTION_TAG_PROCEDURE          "Procedure: "
/* Special tag indicating end of action */
#define ACTION_TAG_END                "End"
/* This is not an action code but an information which
   may relate to several actions */
#define ACTION_TAG_CLEANING_LEVELS    "Cleaning Levels: "
/* Internal Operations which are postphoned */
#define ACTION_CODE_NEW_WINDOW         -31
/* This is not an action code but an information which
   may relate to several actions */
#define ACTION_CODE_CLEANING_LEVELS    -3
#define ACTION_CODE_WRONG              -2
#define ACTION_CODE_UNDETERMINED       -1
#define ACTION_CODE_GET_CARTRIDGE_INFO 0
#define ACTION_CODE_CHANGE_CARTRIDGE   1
#define ACTION_CODE_CLEAN_HEAD         2
#define ACTION_CODE_GET_STATUS         3
#define ACTION_CODE_RESET_PRINTER      4
#define ACTION_CODE_CALIBRATE_HEAD     5
#define ACTION_CODE_PRINT_TESTPATTERN  6
#define ACTION_CODE_CHANGE_HEAD        7
#define ACTION_CODE_SWITCH_PRINTER     8
#define ACTION_CODE_INIT               30
#define ACTION_CODE_PROCEDURE          31

/* Functions */
#define FUNCTION_TAG_SEND   "send "
#define FUNCTION_TAG_GET    "get "
#define FUNCTION_TAG_SHOW   "show "
#define FUNCTION_TAG_ASK    "ask"
#define FUNCTION_TAG_SPLIT  "split "
#define FUNCTION_TAG_EVAL   "eval "
#define FUNCTION_TAG_CONDGO "cgorel "
#define FUNCTION_TAG_HIDE   "hide "
#define FUNCTION_TAG_SYSTEM "system "
#define FUNCTION_TAG_ACTION "action "

/* Indicates whether Toolbox must be updated.
   Each Bit indicates a certain update event.
   After synchronization all Bits are cleared. */
#define UPDATE_NO_UPDATES       0
#define UPDATE_MESSAGE          1
#define UPDATE_ERROR_STATUS     2
#define UPDATE_DISPLAY          4

/* internal Errors range from 0x20 ownwards */
#define ERROR_UNKNOWN_VARIABLE                32
#define ERROR_CONTROL_FILE                    33
#define ERROR_UNKNOWN_FUNCTION                34
#define ERROR_UNKNOWN_ACTION                  35
#define ERROR_ACTION_NOT_AVAILABLE            36
#define ERROR_NO_ACTION                       37
#define ERROR_MISSING_FUNCTION_PARAMETER      38
#define ERROR_INVALID_RISC_OS_EVAL_EXPRESSION 39
#define ERROR_UNABLE_TO_SEND_TO_PRINTER       40
#define ERROR_UNABLE_TO_RECEIVE_FROM_PRINTER  41
#define ERROR_INVALID_FUNCTION_PARAMETER      42
#define ERROR_DRAW_FILE_IMPORT_FAILED         43
#define ERROR_WRONG_VARIABLE_TYPE             44

#define MAX_OVERLAYS         10
#define MAX_PROCEDURES       32
#define MAX_LOOP_DEPTH       10
#define MAX_USER_OPERATION   29

/* !!!!!!!!!! data structures !!!!!!!!!! */
struct information_struct {
struct printer_struct *printer;
struct variable_struct general_variables;
char used_manufacturer[MAX_MESSAGE_LENGTH];
char used_printer_type[MAX_MESSAGE_LENGTH];
char *name_of_control_file;
unsigned long available_actions;
unsigned long available_cartridge_change[(MAX_CARTRIDGE/32)+1];
unsigned long available_clean_head[(MAX_CARTRIDGE/32)+1];
unsigned long available_calibrate_head[(MAX_CARTRIDGE/32)+1];
unsigned long available_head_change[(MAX_CARTRIDGE/32)+1];
unsigned long available_procedure[(MAX_PROCEDURES/32)+1];
unsigned long available_cleaning_levels;
int mode_get_status;
int mode_get_reset;
int paper_flag;
int selected_flag;
int no_error_flag;
struct draw_file_struct loaded_file[MAX_OVERLAYS];
struct error_struct error;
/* Dialogue indicator */
bool dialogue_flag;
fpos_t action_continue_position[MAX_LOOP_DEPTH];
fpos_t action_start_position[MAX_LOOP_DEPTH];
int action_function_line[MAX_LOOP_DEPTH];
int action_stack_pos;
int subaction_code;
int sub_selection;
/* Synchronisation information */
unsigned long update_information;
FILE *trace;
};

/* !!!!!!!!!! support functions !!!!!!!!!! */
void update_variable_tb(struct information_struct *information,
                        void *variable)
{

if (!information) return;
if (variable == (&information->general_variables.error_status)) {
  information->update_information|=UPDATE_ERROR_STATUS;
  }
else if (variable == (information->general_variables.message)) {
  information->update_information|=UPDATE_MESSAGE;
  }
}

/* !!!!!!!!!! functions !!!!!!!!!! */
bool initialize_information(struct information_struct *information)
{
int i;

if (!information) return false;
initialize_variables(&information->general_variables);
information->name_of_control_file=NULL;
for (i=0; i < MAX_OVERLAYS; i++) {
  initialize_draw_file(&information->loaded_file[i]);
  }
information->dialogue_flag=false;
information->subaction_code=ACTION_CODE_UNDETERMINED;
information->action_stack_pos=0;
information->update_information=UPDATE_NO_UPDATES;
information->trace=NULL;
return true;
}

bool drop_information(struct information_struct *information)
{
int i;

if (!information) return false;
for (i=0; i < MAX_OVERLAYS; i++) {
  drop_draw_file(&information->loaded_file[i]);
  }
drop_variables(&information->general_variables);
if (information->name_of_control_file) {
  free(information->name_of_control_file);
  }
if (information->trace) {
  fclose(information->trace);
  }
return initialize_information(information);
}

bool get_line(FILE *command_file,
              char *input_line)
{
char *c;

while (!feof(command_file)) {
  strcpy(input_line, "");
  fgets(input_line, MAX_STRING_LENGTH - 1, command_file);
  if (!feof(command_file)) {
    /* ignore comments */
    if (input_line[0] != '#') {
      c=&input_line[strlen(input_line) - 1];
      while ((c >= input_line) &&
             ((*c == '\n') ||
              (*c == '\r') ||
              (isspace(*c)))) {
        *c='\0';
        c--;
        }
      return true;
      }
    }
  }
return false;
}

int determine_mode(char *c)
{

while (isspace(*c)) {
  c++;
  }
if (strcmp(c, ACTION_MODE_TAG_NOT_SPECIAL) == 0) {
  return CONNECTION_NOT_SPECIAL;
  }
else if (strcmp(c, ACTION_MODE_TAG_PARALLEL) == 0) {
  return CONNECTION_PARALLEL;
  }
else if (strcmp(c, ACTION_MODE_TAG_USB) == 0) {
  return CONNECTION_USB;
  }
return CONNECTION_INDETERMINED;
}

int determine_command(char *input_line,
                      int *parameter_value,
                      bool command_mode)
{
int i;
int space_suppress;
char *c;

if (!input_line) return ACTION_CODE_UNDETERMINED;
if (parameter_value) {
  /* initalize parameter to avoid problems at limit check */
  *parameter_value=0;
  }
if (command_mode) {
  space_suppress=-1;
  }
else {
  space_suppress=0;
  }
if (strncmp(input_line, ACTION_TAG_GET_CARTRIDGE_INFO, strlen(ACTION_TAG_GET_CARTRIDGE_INFO)) == 0) {
  return ACTION_CODE_GET_CARTRIDGE_INFO;
  }
else if (strncmp(input_line, ACTION_TAG_CHANGE_CARTRIDGE, strlen(ACTION_TAG_CHANGE_CARTRIDGE)) == 0) {
  c=input_line + strlen(ACTION_TAG_CHANGE_CARTRIDGE);
  if (sscanf(c,
             "%x",
             &i) == 1) {
    if ((MIN_COLOUR <= i) &&
        (i <= MAX_CARTRIDGE)) {
      if (parameter_value) {
        *parameter_value=i;
        }
      return ACTION_CODE_CHANGE_CARTRIDGE;
      }
    else {
      return ACTION_CODE_WRONG;
      }
    }
  else {
    return ACTION_CODE_WRONG;
    }
  }
else if (strncmp(input_line, ACTION_TAG_CLEAN_HEAD, strlen(ACTION_TAG_CLEAN_HEAD)) == 0) {
  c=input_line + strlen(ACTION_TAG_CLEAN_HEAD);
  if (sscanf(c,
             "%x",
             &i) == 1) {
    if ((MIN_COLOUR <= i) &&
        (i <= MAX_CARTRIDGE)) {
      if (parameter_value) {
        *parameter_value=i;
        }
      return ACTION_CODE_CLEAN_HEAD;
      }
    else {
      return ACTION_CODE_WRONG;
      }
    }
  else {
    return ACTION_CODE_WRONG;
    }
  }
else if (strncmp(input_line, ACTION_TAG_GET_STATUS, strlen(ACTION_TAG_GET_STATUS) + space_suppress) == 0) {
  if (command_mode) {
    if (parameter_value) {
      *parameter_value=CONNECTION_NOT_SPECIAL;
      }
    }
  else {
    if ((i=determine_mode(input_line + strlen(ACTION_TAG_GET_STATUS) + space_suppress)) == CONNECTION_INDETERMINED) {
      return ACTION_CODE_WRONG;
      }
    if (parameter_value) {
      *parameter_value=i;
      }
    }
  return ACTION_CODE_GET_STATUS;
  }
else if (strncmp(input_line, ACTION_TAG_RESET_PRINTER, strlen(ACTION_TAG_RESET_PRINTER) + space_suppress) == 0) {
  if (command_mode) {
    if (parameter_value) {
      *parameter_value=CONNECTION_NOT_SPECIAL;
      }
    }
  else {
    if ((i=determine_mode(input_line + strlen(ACTION_TAG_RESET_PRINTER) + space_suppress)) == CONNECTION_INDETERMINED) {
      return ACTION_CODE_WRONG;
      }
    if (parameter_value) {
      *parameter_value=i;
      }
    }
  return ACTION_CODE_RESET_PRINTER;
  }
else if (strncmp(input_line, ACTION_TAG_CALIBRATE_HEAD, strlen(ACTION_TAG_CALIBRATE_HEAD)) == 0) {
  c=input_line + strlen(ACTION_TAG_CALIBRATE_HEAD);
  if (sscanf(c,
             "%x",
             &i) == 1) {
    if ((MIN_COLOUR <= i) &&
        (i <= MAX_CARTRIDGE)) {
      if (parameter_value) {
        *parameter_value=i;
        }
      return ACTION_CODE_CALIBRATE_HEAD;
      }
    else {
      return ACTION_CODE_WRONG;
      }
    }
  else {
    return ACTION_CODE_WRONG;
    }
  }
else if (strncmp(input_line, ACTION_TAG_PRINT_TESTPATTERN, strlen(ACTION_TAG_PRINT_TESTPATTERN)) == 0) {
  return ACTION_CODE_PRINT_TESTPATTERN;
  }
else if (strncmp(input_line, ACTION_TAG_CHANGE_HEAD, strlen(ACTION_TAG_CHANGE_HEAD)) == 0) {
  c=input_line + strlen(ACTION_TAG_CHANGE_HEAD);
  if (sscanf(c,
             "%x",
             &i) == 1) {
    if ((MIN_COLOUR <= i) &&
        (i <= MAX_CARTRIDGE)) {
      if (parameter_value) {
        *parameter_value=i;
        }
      return ACTION_CODE_CHANGE_HEAD;
      }
    else {
      return ACTION_CODE_WRONG;
      }
    }
  else {
    return ACTION_CODE_WRONG;
    }
  }
else if (strncmp(input_line, ACTION_TAG_SWITCH_PRINTER, strlen(ACTION_TAG_SWITCH_PRINTER)) == 0) {
  return ACTION_CODE_SWITCH_PRINTER;
  }
else if (strncmp(input_line, ACTION_TAG_INIT, strlen(ACTION_TAG_INIT)) == 0) {
  return ACTION_CODE_INIT;
  }
else if (strncmp(input_line, ACTION_TAG_PROCEDURE, strlen(ACTION_TAG_PROCEDURE)) == 0) {
  c=input_line + strlen(ACTION_TAG_PROCEDURE);
  if (sscanf(c,
             "%x",
             &i) == 1) {
    if ((0 <= i) &&
        (i <= MAX_PROCEDURES)) {
      if (parameter_value) {
        *parameter_value=i;
        }
      return ACTION_CODE_PROCEDURE;
      }
    else {
      return ACTION_CODE_WRONG;
      }
    }
  else {
    return ACTION_CODE_WRONG;
    }
  }
else if (strncmp(input_line, ACTION_TAG_CLEANING_LEVELS, strlen(ACTION_TAG_CLEANING_LEVELS)) == 0) {
  c=input_line + strlen(ACTION_TAG_CLEANING_LEVELS);
  if (sscanf(c,
             "%x",
             &i) == 1) {
    if (i != 0) {
      if (parameter_value) {
        *parameter_value=i;
        }
      return ACTION_CODE_CLEANING_LEVELS;
      }
    else {
      return ACTION_CODE_WRONG;
      }
    }
  else {
    return ACTION_CODE_WRONG;
    }
  }
return ACTION_CODE_UNDETERMINED;
}

bool determine_commands(struct information_struct *information)
{
int i, action;
char input_line[MAX_STRING_LENGTH];
FILE *command_file;

if (!information) return false;
information->available_actions=0;
information->mode_get_status=CONNECTION_NOT_SPECIAL;
information->mode_get_reset=CONNECTION_NOT_SPECIAL;
for (i=0; i < ((MAX_CARTRIDGE/32) + 1); i++) {
  information->available_cartridge_change[i]=0;
  information->available_clean_head[i]=0;
  information->available_calibrate_head[i]=0;
  information->available_head_change[i]=0;
  }
for (i=0; i < ((MAX_PROCEDURES/32) + 1); i++) {
  information->available_procedure[i]=0;
  }
information->available_cleaning_levels=0;
if (!(command_file=fopen(information->name_of_control_file, "r"))) {
  information->error.number=ERROR_CONTROL_FILE;
  information->error.number_of_parameters=1;
  strcpy(information->error.parameter[0], information->name_of_control_file);
  return false;
  }
while (!feof(command_file)) {
  if (get_line(command_file,
               input_line)) {
    action=determine_command(input_line,
                             &i,
                             false);
    switch(action) {
      case ACTION_CODE_CLEANING_LEVELS: {
        /* Only a common information and not an action */
        information->available_cleaning_levels=(unsigned long) i;
        }
      break;
      case ACTION_CODE_WRONG: {
        /* ignore Errors */
        }
      break;
      case ACTION_CODE_UNDETERMINED: {
        /* ignore undetermined stuff */
        }
      break;
      case ACTION_CODE_CHANGE_CARTRIDGE: {
        information->available_cartridge_change[i/32]|=1<<(i%32);
        information->available_actions|=1<<action;
        }
      break;
      case ACTION_CODE_CLEAN_HEAD: {
        information->available_clean_head[i/32]|=1<<(i%32);
        information->available_actions|=1<<action;
        if (information->available_cleaning_levels == 0) {
          information->available_cleaning_levels=1;
          }
        }
      break;
      case ACTION_CODE_GET_STATUS: {
        if (information->printer->real_connection_type == i) {
          information->mode_get_status=i;
          information->available_actions|=1<<action;
          }
        else if (i == CONNECTION_NOT_SPECIAL) {
          information->available_actions|=1<<action;
          }
        }
      break;
      case ACTION_CODE_RESET_PRINTER: {
        if (information->printer->real_connection_type == i) {
          information->mode_get_reset=i;
          information->available_actions|=1<<action;
          }
        else if (i == CONNECTION_NOT_SPECIAL) {
          information->available_actions|=1<<action;
          }
        }
      break;
      case ACTION_CODE_CALIBRATE_HEAD: {
        information->available_calibrate_head[i/32]|=1<<(i%32);
        information->available_actions|=1<<action;
        }
      break;
      case ACTION_CODE_CHANGE_HEAD: {
        information->available_head_change[i/32]|=1<<(i%32);
        information->available_actions|=1<<action;
        }
      break;
      case ACTION_CODE_SWITCH_PRINTER: {
        information->available_actions|=1<<action;
        }
      break;
      case ACTION_CODE_PROCEDURE: {
        information->available_procedure[i/32]|=1<<(i%32);
        information->available_actions|=1<<action;
        }
      break;
      default: {
        information->available_actions|=1<<action;
        }
      }
    }
  }
fclose(command_file);
/* Select minimal possible cleaning level */
for (i=31; i >= 0; i--) {
  if ((information->available_cleaning_levels & (1<<i)) != 0) {
    information->general_variables.cleaning_level=i + 1;
    }
  }
if (information->trace) {
  fprintf(information->trace,
          "available actions          : %lx\n",
          information->available_actions);
  for (i=0; i < ((MAX_CARTRIDGE/32) + 1); i++) {
    fprintf(information->trace,
            "available cartridge changes: %d %lx\n",
            i,
            information->available_cartridge_change[i]);
    }
  for (i=0; i < ((MAX_CARTRIDGE/32) + 1); i++) {
    fprintf(information->trace,
            "available head cleans      : %d %lx\n",
            i,
            information->available_clean_head[i]);
    }
  for (i=0; i < ((MAX_CARTRIDGE/32) + 1); i++) {
    fprintf(information->trace,
            "available head cleans      : %d %lx\n",
            i,
            information->available_calibrate_head[i]);
    }
  fprintf(information->trace,
          "cleaning levels            : %d %lx\n",
          i,
          information->available_cleaning_levels);
  for (i=0; i < ((MAX_CARTRIDGE/32) + 1); i++) {
    fprintf(information->trace,
            "available head changes     : %d %lx\n",
            i,
            information->available_head_change[i]);
    }
  for (i=0; i < ((MAX_PROCEDURES/32) + 1); i++) {
    fprintf(information->trace,
            "available procedures       : %d %lx\n",
            i,
            information->available_procedure[i]);
    }
  }
return true;
}

char *get_parameter(char **input_line)
{
char *c;

if (!input_line) return NULL;
while(isspace(**input_line)) (*input_line)++;
if (**input_line == '\0') {
  *input_line=0;
  return NULL;
  }
c=strchr(*input_line, ' ');
if (c) {
  *c='\0';
  c++;
  while((*c != '\0') &&
        isspace(*c)) c++;
  return c;
  }
else {
  return c;
  }
}

char *prepare_expression(struct information_struct *information,
                         char *expression,
                         char *error_hint)
{
static char result_string[MAX_STRING_LENGTH];
char parameter_string[MAX_STRING_LENGTH];
char *pnext, *actual_ps_pos;
void *variable;
int type_of_variable;
int size_of_variable;
char separator[2];
_kernel_swi_regs regs;

if (!information) return NULL;
if (!expression) {
  information->error.number=ERROR_WRONG_CALL;
  information->error.number_of_parameters=2;
  strcpy(information->error.parameter[0], "prepare_expression");
  strcpy(information->error.parameter[1], "expression");
  return NULL;
  }
/* Get parameters */
strcpy(parameter_string, "");
strcpy(separator, "");
actual_ps_pos=parameter_string;
while((pnext=get_parameter(&expression)) != NULL) {
  /* separator is mandatory at end of variable list */
  if (strcmp(expression, "#") == 0) break;
  /* Interpret as variable */
  if ((variable=get_address_of_variable(&information->general_variables,
                                        expression,
                                        &type_of_variable,
                                        &size_of_variable)) == NULL) {
    information->error.number=ERROR_UNKNOWN_VARIABLE;
    information->error.number_of_parameters=1;
    strcpy(information->error.parameter[0], expression);
    return NULL;
    }
  switch(type_of_variable) {
    case VARIABLE_TYPE_CHAR: {
      char *c;

      /* Must be changed to RISC OS eval expression
         0       -> C-String terminator
         10      -> Space
         13      -> Space
         34      -> ""
         60      -> <&Hex>
         1-31    -> <&Hex>
         128-159 -> <&Hex>
         Else    -> Unchanged */
      sprintf(actual_ps_pos,
              "%s\"",
              separator);
      while(*actual_ps_pos != '\0') actual_ps_pos++;
      c=variable;
      while (*c != '\0') {
        if ((*c == '\n') ||
            (*c == '\r')) {
          *actual_ps_pos=' ';
          actual_ps_pos++;
          }
        else if (*c == '"') {
          strcpy(actual_ps_pos, "\"\"");
          actual_ps_pos+=2;
          }
        else if (((*c >= 1) &&
                  (*c <= 31)) ||
                 ((*c >= 128) &&
                  (*c <= 159)) ||
                 (*c == 60))  {
          sprintf(actual_ps_pos,
                  "<&%2x>",
                  *c);
          actual_ps_pos+=5;
          }
        else {
          *actual_ps_pos=*c;
          actual_ps_pos++;
          }
        c++;
        }
      *actual_ps_pos='"';
      actual_ps_pos++;
      *actual_ps_pos='\0';
      strcpy(separator, " ");
      }
    break;
    case VARIABLE_TYPE_INTEGER: {
      sprintf(actual_ps_pos,
              "%s%d",
              separator,
              *((int *) variable));
      while(*actual_ps_pos != '\0') actual_ps_pos++;
      strcpy(separator, " ");
      }
    break;
    }
  expression=pnext;
  }
if (!pnext) {
  information->error.number=ERROR_MISSING_FUNCTION_PARAMETER;
  information->error.number_of_parameters=1;
  strcpy(information->error.parameter[0], error_hint);
  return NULL;
  }
regs.r[0]=(int) parameter_string;
regs.r[1]=(int) result_string;
regs.r[2]=MAX_STRING_LENGTH-1;
regs.r[3]=(int) pnext;
regs.r[4]=strlen(pnext);
/* Prepared for OS_SubstituteArgs32 */
regs.r[5]=0;
/* Use OS_SubstituteArgs insetad of OS_SubstituteArgs32 because
   it is an application and so the stack won't use Bit 31 and
   OS_SubstituteArgs32 is not available on a Risc PC */
_kernel_swi(OS_SubstituteArgs, &regs, &regs);
result_string[regs.r[2] - 1]='\0';
return result_string;
}

bool execute_function(struct information_struct *information,
                      char *input_line,
                      int *ignore_counter)
{
char *c;
char *pnext;

if (!information) return false;
if (ignore_counter) {
  *ignore_counter=0;
  }
if (information->trace) {
  fprintf(information->trace,
          "execute_function: %s\n",
          input_line);
  }
if (strncmp(input_line, FUNCTION_TAG_SEND, strlen(FUNCTION_TAG_SEND)) == 0) {
  int size;
  char sequence[MAX_RESPONSE_LENGTH];
  char *value_start;

  /* Scan sequence character by character.
     Format is the same as used inside RISC OS PDFs. */
  c=input_line + strlen(FUNCTION_TAG_SEND);
  while (isspace(*c)) c++;
  size=0;
  value_start=c;
  while (*c != '\0') {
    if (*c == ',') {
      *c='\0';
      if (*value_start == '\"') {
        sequence[size]=*(value_start + 1);
        size++;
        }
      else {
        sequence[size]=atoi(value_start);
        size++;
        }
      value_start=c + 1;
      while (isspace(*value_start)) value_start++;
      }
    c++;
    }
  if (*value_start == '\"') {
    sequence[size]=*(value_start + 1);
    size++;
    }
  else {
    sequence[size]=atoi(value_start);
    size++;
    }
  /* Write sequence to printer */
  if (!printer_send(information->printer,
                    sequence,
                    size)) {
    information->error.number=ERROR_UNABLE_TO_SEND_TO_PRINTER;
    information->error.number_of_parameters=2;
    strcpy(information->error.parameter[0], information->printer->output_file);
    strcpy(information->error.parameter[1], input_line);
    return false;
    }
  }
else if (strncmp(input_line, FUNCTION_TAG_GET, strlen(FUNCTION_TAG_GET)) == 0) {
  unsigned long size;
  int terminator;

  c=input_line + strlen(FUNCTION_TAG_GET);
  pnext=get_parameter(&c);
  if (!c) {
    /* Default is NL */
    terminator='\n';
    }
  else {
    if (sscanf(c,
               "%x",
               &terminator) < 1) {
      information->error.number=ERROR_INVALID_FUNCTION_PARAMETER;
      information->error.number_of_parameters=2;
      strcpy(information->error.parameter[0], "P1");
      strcpy(information->error.parameter[1], input_line);
      return false;
      }
    }
  if (information->trace) {
    fprintf(information->trace,
            "message terminator: %d\n",
            terminator);
    }
  /* Read sequence from printer */
  size=MAX_RESPONSE_LENGTH;
  if (!printer_get(information->printer,
                   terminator,
                   information->general_variables.response,
                   &size)) {
    information->error.number=ERROR_UNABLE_TO_RECEIVE_FROM_PRINTER;
    information->error.number_of_parameters=2;
    strcpy(information->error.parameter[0], information->printer->input_file);
    strcpy(information->error.parameter[1], input_line);
    return false;
    }
  if (information->trace) {
    c=information->general_variables.response;
    fprintf(information->trace,
            "length of received message: %ld\n",
            size);
    while(*c != '\0') {
      fprintf(information->trace,
              " %2x %c\n",
              *c,
              *c);
      c++;
      }
    }
  }
else if (strncmp(input_line, FUNCTION_TAG_SHOW, strlen(FUNCTION_TAG_SHOW)) == 0) {
  int act_overlay;

  act_overlay=0;
  c=input_line + strlen(FUNCTION_TAG_SHOW);
  pnext=get_parameter(&c);
  if (!pnext ||
      !c) {
    information->error.number=ERROR_MISSING_FUNCTION_PARAMETER;
    information->error.number_of_parameters=1;
    strcpy(information->error.parameter[0], input_line);
    return false;
    }
  act_overlay=atoi(c);
  if ((act_overlay < 0) ||
      (act_overlay >= MAX_OVERLAYS)) {
    information->error.number=ERROR_INVALID_FUNCTION_PARAMETER;
    information->error.number_of_parameters=2;
    strcpy(information->error.parameter[0], "P1");
    strcpy(information->error.parameter[1], input_line);
    return false;
    }
  c=pnext;
  if (!load_draw_file(&information->loaded_file[act_overlay],
                      c)) {
    information->error.number=ERROR_DRAW_FILE_IMPORT_FAILED;
    information->error.number_of_parameters=2;
    strcpy(information->error.parameter[0], input_line);
    strcpy(information->error.parameter[1], c);
    return false;
    }
  prepare_draw_file(&information->loaded_file[act_overlay],
                    &information->general_variables);
  information->update_information|=UPDATE_DISPLAY;
  }
else if (strncmp(input_line, FUNCTION_TAG_ASK, strlen(FUNCTION_TAG_ASK)) == 0) {
  int i;

  c=input_line + strlen(FUNCTION_TAG_ASK);
  pnext=get_parameter(&c);
  i=0;
  while(c &&
        i < MAX_BUTTONS) {
    strcpy(information->general_variables.dialog_button[i], c);
    i++;
    c=pnext;
    pnext=get_parameter(&c);
    }
  /* Setting this flag in conjunction with unsuccesful return
     causes a break in command execution until a button is pressed */
  information->dialogue_flag=true;
  return false;
  }
else if (strncmp(input_line, FUNCTION_TAG_SPLIT, strlen(FUNCTION_TAG_SPLIT)) == 0) {
  void *variable[20];
  int type_of_variable[20];
  int size_of_variable[20];
  int no_of_variables;
  int real_assigned_variables;
  int type_of_source_variable;
  int i;
  char *source;
  char dummy[256];

  c=input_line + strlen(FUNCTION_TAG_SPLIT);
  pnext=get_parameter(&c);
  if (!pnext ||
      !c) {
    information->error.number=ERROR_MISSING_FUNCTION_PARAMETER;
    information->error.number_of_parameters=1;
    strcpy(information->error.parameter[0], input_line);
    return false;
    }
  if ((source=get_address_of_variable(&information->general_variables,
                                      c,
                                      &type_of_source_variable,
                                      NULL)) == NULL) {
    information->error.number=ERROR_UNKNOWN_VARIABLE;
    information->error.number_of_parameters=1;
    strcpy(information->error.parameter[0], c);
    return false;
    }
  if (type_of_source_variable != VARIABLE_TYPE_CHAR) {
    information->error.number=ERROR_WRONG_VARIABLE_TYPE;
    information->error.number_of_parameters=3;
    strcpy(information->error.parameter[0], c);
    strcpy(information->error.parameter[1], input_line);
    strcpy(information->error.parameter[2], "!= character");
    return false;
    }
  c=pnext;
  /* List of all variables inside which pattern results must be stored */
  no_of_variables=0;
  /* Filter variables from pattern and put pointers on them into list */
  while((pnext=get_parameter(&c)) != NULL) {
    /* separator is mandatory at end of variable list */
    if (strcmp(c, "#") == 0) break;
    if (no_of_variables >= 20) {
      information->error.number=ERROR_INVALID_FUNCTION_PARAMETER;
      information->error.number_of_parameters=2;
      strcpy(information->error.parameter[0], "P2 .. Px > 20");
      strcpy(information->error.parameter[1], input_line);
      return false;
      }
    /* Interpret as variable */
    if ((variable[no_of_variables]=get_address_of_variable(&information->general_variables,
                                                           c,
                                                           &type_of_variable[no_of_variables],
                                                           &size_of_variable[no_of_variables])) == NULL) {
      information->error.number=ERROR_UNKNOWN_VARIABLE;
      information->error.number_of_parameters=1;
      strcpy(information->error.parameter[0], c);
      return false;
      }
    no_of_variables++;
    c=pnext;
    }
  if (!pnext) {
    information->error.number=ERROR_MISSING_FUNCTION_PARAMETER;
    information->error.number_of_parameters=1;
    strcpy(information->error.parameter[0], input_line);
    return false;
    }
  if (no_of_variables == 0) {
    information->error.number=ERROR_MISSING_FUNCTION_PARAMETER;
    information->error.number_of_parameters=1;
    strcpy(information->error.parameter[0], input_line);
    return false;
    }
  for (i=no_of_variables; i < 20; i++) {
    variable[i]=dummy;
    }
  /* Do scanf for maximal amount of arguments.
     Pointer type is not correct in all cases.
     At unused arguments pointers to a dummy
     are passed. */
  real_assigned_variables=sscanf(source,
                                 pnext,
                                 variable[0],
                                 variable[1],
                                 variable[2],
                                 variable[3],
                                 variable[4],
                                 variable[5],
                                 variable[6],
                                 variable[7],
                                 variable[8],
                                 variable[9],
                                 variable[10],
                                 variable[11],
                                 variable[12],
                                 variable[13],
                                 variable[14],
                                 variable[15],
                                 variable[16],
                                 variable[17],
                                 variable[18],
                                 variable[19]);
  if (real_assigned_variables > no_of_variables) {
    real_assigned_variables=no_of_variables;
    }
  for (i=0; i < no_of_variables; i++) {
    if (information->trace) {
      switch(type_of_variable[i]) {
        case VARIABLE_TYPE_CHAR: {
          fprintf(information->trace,
                  "result: %s\n",
                  variable[i]);
          }
        break;
        case VARIABLE_TYPE_INTEGER: {
          fprintf(information->trace,
                  "result: %ld\n",
                  *((unsigned long *) variable[i]));
          }
        break;
        }
      }
    update_variable_tb(information,
                       variable[i]);
    }
  }
else if (strncmp(input_line, FUNCTION_TAG_EVAL, strlen(FUNCTION_TAG_EVAL)) == 0) {
  void *target;
  int type_of_variable;
  int size_of_variable;
  char result[MAX_RESPONSE_LENGTH];
  char *command;
  _kernel_swi_regs regs;

  c=input_line + strlen(FUNCTION_TAG_EVAL);
  pnext=get_parameter(&c);
  if (!pnext ||
      !c) {
    information->error.number=ERROR_MISSING_FUNCTION_PARAMETER;
    information->error.number_of_parameters=1;
    strcpy(information->error.parameter[0], input_line);
    return false;
    }
  if ((target=get_address_of_variable(&information->general_variables,
                                      c,
                                      &type_of_variable,
                                      &size_of_variable)) == NULL) {
    information->error.number=ERROR_UNKNOWN_VARIABLE;
    information->error.number_of_parameters=1;
    strcpy(information->error.parameter[0], c);
    return false;
    }
  if ((command=prepare_expression(information,
                                  pnext,
                                  input_line)) == NULL) {
    /* Error already set inside function */
    return false;
    }
  regs.r[0]=(int) command;
  if (type_of_variable == VARIABLE_TYPE_INTEGER) {
    regs.r[1]=NULL;
    regs.r[2]=0;
    }
  else {
    regs.r[1]=(int) result;
    regs.r[2]=MAX_RESPONSE_LENGTH - 1;
    }
  if (_kernel_swi(OS_EvaluateExpression, &regs, &regs) != 0) {
    information->error.number=ERROR_INVALID_RISC_OS_EVAL_EXPRESSION;
    information->error.number_of_parameters=2;
    strcpy(information->error.parameter[0], command);
    strcpy(information->error.parameter[1], input_line);
    return false;
    }
  if (type_of_variable == VARIABLE_TYPE_INTEGER) {
     *((int *) target)=regs.r[2];
    }
  else {
    result[regs.r[2]]='\0';
    *(((char *)target) + size_of_variable - 1)='\0';
    strncpy((char *) target, result, size_of_variable);
    }
  update_variable_tb(information,
                     target);
  }
else if (strncmp(input_line, FUNCTION_TAG_CONDGO, strlen(FUNCTION_TAG_CONDGO)) == 0) {
  int lines_to_ignore;
  char *command;
  _kernel_swi_regs regs;

  c=input_line + strlen(FUNCTION_TAG_CONDGO);
  pnext=get_parameter(&c);
  if (!pnext ||
      !c) {
    information->error.number=ERROR_MISSING_FUNCTION_PARAMETER;
    information->error.number_of_parameters=1;
    strcpy(information->error.parameter[0], input_line);
    return false;
    }
  lines_to_ignore=atoi(c);
  if (lines_to_ignore == 0) {
    lines_to_ignore=0;
    information->error.number=ERROR_INVALID_FUNCTION_PARAMETER;
    information->error.number_of_parameters=2;
    strcpy(information->error.parameter[0], "P1 = 0");
    strcpy(information->error.parameter[1], input_line);
    return false;
    }
  if ((command=prepare_expression(information,
                                  pnext,
                                  input_line)) == NULL) {
    /* Error already set inside function */
    return false;
    }
  regs.r[0]=(int) command;
  regs.r[1]=NULL;
  regs.r[2]=0;
  if (_kernel_swi(OS_EvaluateExpression, &regs, &regs) != 0) {
    information->error.number=ERROR_INVALID_RISC_OS_EVAL_EXPRESSION;
    information->error.number_of_parameters=2;
    strcpy(information->error.parameter[0], command);
    strcpy(information->error.parameter[1], input_line);
    return false;
    }
  if (regs.r[2] == 0) {
    /* Result false. No jump */
    lines_to_ignore=0;
    }
  if (ignore_counter) {
    *ignore_counter=lines_to_ignore;
    }
  else {
    information->error.number=ERROR_WRONG_CALL;
    information->error.number_of_parameters=2;
    strcpy(information->error.parameter[0], "execute_function");
    strcpy(information->error.parameter[1], "ignore counter");
    return false;
    }
  }
else if (strncmp(input_line, FUNCTION_TAG_HIDE, strlen(FUNCTION_TAG_HIDE)) == 0) {
  int min_overlay, max_overlay;
  int i;

  min_overlay=0;
  c=input_line + strlen(FUNCTION_TAG_HIDE);
  pnext=get_parameter(&c);
  if (!c) {
    information->error.number=ERROR_MISSING_FUNCTION_PARAMETER;
    information->error.number_of_parameters=1;
    strcpy(information->error.parameter[0], input_line);
    return false;
    }
  min_overlay=atoi(c);
  if ((min_overlay < 0) ||
      (min_overlay >= MAX_OVERLAYS)) {
    information->error.number=ERROR_INVALID_FUNCTION_PARAMETER;
    information->error.number_of_parameters=2;
    strcpy(information->error.parameter[0], "P1");
    strcpy(information->error.parameter[1], input_line);
    return false;
    }
  max_overlay=min_overlay;
  if (pnext) {
    if (strcmp(pnext, "upwards") == 0) {
      max_overlay=MAX_OVERLAYS - 1;
      }
    else {
      information->error.number=ERROR_INVALID_FUNCTION_PARAMETER;
      information->error.number_of_parameters=2;
      strcpy(information->error.parameter[0], "P2");
      strcpy(information->error.parameter[1], input_line);
      return false;
      }
    }
  for (i=min_overlay; i < max_overlay; i++) {
    drop_draw_file(&information->loaded_file[i]);
    }
  information->update_information|=UPDATE_DISPLAY;
  }
else if (strncmp(input_line, FUNCTION_TAG_SYSTEM, strlen(FUNCTION_TAG_SYSTEM)) == 0) {
  char *command;

  c=input_line + strlen(FUNCTION_TAG_SYSTEM);
  while (isspace(*c)) c++;
  if (*c == '\0') {
    information->error.number=ERROR_MISSING_FUNCTION_PARAMETER;
    information->error.number_of_parameters=1;
    strcpy(information->error.parameter[0], input_line);
    return false;
    }
  if ((command=prepare_expression(information,
                                  c,
                                  input_line)) == NULL) {
    /* Error already set inside function */
    return false;
    }
  system(command);
  }
else if (strncmp(input_line, FUNCTION_TAG_ACTION, strlen(FUNCTION_TAG_ACTION)) == 0) {
  c=input_line + strlen(FUNCTION_TAG_ACTION);
  while (isspace(*c)) c++;
  if (*c == '\0') {
    information->error.number=ERROR_MISSING_FUNCTION_PARAMETER;
    information->error.number_of_parameters=1;
    strcpy(information->error.parameter[0], input_line);
    return false;
    }
  information->subaction_code=determine_command(c,
                                                &information->sub_selection,
                                                true);
  if ((information->subaction_code == ACTION_CODE_UNDETERMINED) ||
      (information->subaction_code == ACTION_CODE_WRONG)) {
    /* Must be reset to disable sub action handling */
    information->subaction_code=ACTION_CODE_UNDETERMINED;
    information->error.number=ERROR_INVALID_FUNCTION_PARAMETER;
    information->error.number_of_parameters=2;
    strcpy(information->error.parameter[0], "P1");
    strcpy(information->error.parameter[1], input_line);
    return false;
    }
  /* leads to sub action handling in conjunction with subaction code */
  return false;
  }
else {
  information->error.number=ERROR_UNKNOWN_FUNCTION;
  information->error.number_of_parameters=1;
  strcpy(information->error.parameter[0], input_line);
  return false;
  }
return true;
}

bool check_action(struct information_struct *information,
                  int action,
                  int sub_selection)
{
unsigned long action_code, available_sub_selection;

if (!information) return false;
if (action >= 32) {
  information->error.number=ERROR_ACTION_NOT_AVAILABLE;
  information->error.number_of_parameters=1;
  sprintf(information->error.parameter[0], "%d", action);
  return false;
  }
action_code=1<<action;
if ((information->available_actions & action_code) == 0) {
  information->error.number=ERROR_ACTION_NOT_AVAILABLE;
  information->error.number_of_parameters=1;
  sprintf(information->error.parameter[0], "%d", action);
  return false;
  }
switch(action) {
  case ACTION_CODE_CHANGE_CARTRIDGE: {
    if (sub_selection > MAX_CARTRIDGE) {
      information->error.number=ERROR_ACTION_NOT_AVAILABLE;
      information->error.number_of_parameters=1;
      sprintf(information->error.parameter[0], "%d", action);
      return false;
      }
    available_sub_selection=information->available_cartridge_change[sub_selection/32];
    }
  break;
  case ACTION_CODE_CLEAN_HEAD: {
    if (sub_selection > MAX_CARTRIDGE) {
      information->error.number=ERROR_ACTION_NOT_AVAILABLE;
      information->error.number_of_parameters=1;
      sprintf(information->error.parameter[0], "%d", action);
      return false;
      }
    available_sub_selection=information->available_clean_head[sub_selection/32];
    }
  break;
  case ACTION_CODE_CALIBRATE_HEAD: {
    if (sub_selection > MAX_CARTRIDGE) {
      information->error.number=ERROR_ACTION_NOT_AVAILABLE;
      information->error.number_of_parameters=1;
      sprintf(information->error.parameter[0], "%d", action);
      return false;
      }
    available_sub_selection=information->available_calibrate_head[sub_selection/32];
    }
  break;
  case ACTION_CODE_PROCEDURE: {
    if (sub_selection > MAX_PROCEDURES) {
      information->error.number=ERROR_ACTION_NOT_AVAILABLE;
      information->error.number_of_parameters=1;
      sprintf(information->error.parameter[0], "%d", action);
      return false;
      }
    available_sub_selection=information->available_procedure[sub_selection/32];
    }
  break;
  default: {
    return true;
    }
  }
if ((available_sub_selection & (1<<(sub_selection%32))) == 0) {
  information->error.number=ERROR_ACTION_NOT_AVAILABLE;
  information->error.number_of_parameters=1;
  sprintf(information->error.parameter[0], "%d", action);
  return false;
  }
else return true;
}

bool do_action_internal(struct information_struct *information,
                        int action,
                        int sub_selection)
{
int length;
int required_mode;
int ignore_counter;
char command_tag[MAX_STRING_LENGTH];
char input_line[MAX_STRING_LENGTH];
FILE *command_file;

if (!information) return false;
if (information->trace) {
  fprintf(information->trace,
          "action: %d %d\n",
          action,
          sub_selection);
  }
information->dialogue_flag=false;
information->subaction_code=ACTION_CODE_UNDETERMINED;
/* Check whether action exists */
if (!check_action(information,
                  action,
                  sub_selection)) {
  /* Error already set inside function */
  return false;
  }
required_mode=CONNECTION_INDETERMINED;
switch(action) {
  case ACTION_CODE_GET_CARTRIDGE_INFO: {
    strcpy(command_tag, ACTION_TAG_GET_CARTRIDGE_INFO);
    }
  break;
  case ACTION_CODE_CHANGE_CARTRIDGE: {
    sprintf(command_tag,
            "%s%x",
            ACTION_TAG_CHANGE_CARTRIDGE,
            sub_selection);
    }
  break;
  case ACTION_CODE_CLEAN_HEAD: {
    sprintf(command_tag,
            "%s%x",
            ACTION_TAG_CLEAN_HEAD,
            sub_selection);
    }
  break;
  case ACTION_CODE_GET_STATUS: {
    strcpy(command_tag, ACTION_TAG_GET_STATUS);
    required_mode=information->mode_get_status;
    /* Perform standard operation */
    if (!printer_status(information->printer,
                        &information->error,
                        &information->general_variables.error_status)) {
      /* Error already set inside function */
      return false;
      }
    else {
      information->update_information|=UPDATE_ERROR_STATUS;
      }
    }
  break;
  case ACTION_CODE_RESET_PRINTER: {
    strcpy(command_tag, ACTION_TAG_RESET_PRINTER);
    required_mode=information->mode_get_reset;
    /* Perform standard operation */
    if (!printer_reset(information->printer,
                       &information->error)) {
      /* Error already set inside function */
      return false;
      }
    }
  break;
  case ACTION_CODE_CALIBRATE_HEAD: {
    sprintf(command_tag,
            "%s%x",
            ACTION_TAG_CALIBRATE_HEAD,
            sub_selection);
    }
  break;
  case ACTION_CODE_PRINT_TESTPATTERN: {
    strcpy(command_tag, ACTION_TAG_PRINT_TESTPATTERN);
    }
  break;
  case ACTION_CODE_CHANGE_HEAD: {
    sprintf(command_tag,
            "%s%x",
            ACTION_TAG_CHANGE_HEAD,
            sub_selection);
    }
  break;
  case ACTION_CODE_SWITCH_PRINTER: {
    strcpy(command_tag, ACTION_TAG_SWITCH_PRINTER);
    }
  case ACTION_CODE_INIT: {
    strcpy(command_tag, ACTION_TAG_INIT);
    }
  break;
  case ACTION_CODE_PROCEDURE: {
    sprintf(command_tag,
            "%s%x",
            ACTION_TAG_PROCEDURE,
            sub_selection);
    }
  break;
  default: {
    information->error.number=ERROR_UNKNOWN_ACTION;
    information->error.number_of_parameters=1;
    sprintf(information->error.parameter[0],
            "%d",
            action);
    return false;
    }
  }
length=strlen(command_tag);
if (!(command_file=fopen(information->name_of_control_file, "r"))) {
  information->error.number=ERROR_CONTROL_FILE;
  information->error.number_of_parameters=1;
  strcpy(information->error.parameter[0], information->name_of_control_file);
  return false;
  }
while (!feof(command_file)) {
  if (get_line(command_file,
               input_line)) {
    if (strncmp(input_line, command_tag, length) == 0) {
      /* In case that same action is avaliable for different connection types
         the according version must be filtered */
      if ((required_mode == CONNECTION_INDETERMINED) ||
          (required_mode == determine_mode(input_line + length))) {
        /* Required command sequence found. Now execute */
        /* Note actual file position for backwards jumps */
        fgetpos(command_file,
                &information->action_start_position[information->action_stack_pos]);
        information->action_function_line[information->action_stack_pos]=-1;
        ignore_counter=0;
        while (!feof(command_file)) {
          if (get_line(command_file,
                       input_line)) {
            if ((strcmp(input_line, ACTION_TAG_END) == 0) &&
                (ignore_counter >= 0)) {
              /* Action is at end */
              fclose(command_file);
              return true;
              }
            information->action_function_line[information->action_stack_pos]++;
            if (ignore_counter == 0) {
              /* execute commands */
              if (!execute_function(information,
                                    input_line,
                                    &ignore_counter)) {
                /* Interupt mode is not an error */
                if ((information->dialogue_flag) ||
                    (information->subaction_code != ACTION_CODE_UNDETERMINED)) {
                  /* Note actual file position to continue */
                  fgetpos(command_file,
                          &information->action_continue_position[information->action_stack_pos]);
                  fclose(command_file);
                  return false;
                  }
                else {
                  /* Error has been set by function already */
                  fclose(command_file);
                  return false;
                  }
                }
              }
            else if (ignore_counter < 0) {
              /* Rewind to start of action */
              fsetpos(command_file,
                      &information->action_start_position[information->action_stack_pos]);
              /* Calculate lines to skip */
              ignore_counter+=information->action_function_line[information->action_stack_pos] - 1;
              if (ignore_counter < 0) {
                ignore_counter=0;
                }
              information->action_function_line[information->action_stack_pos]=-1;
              }
            else {
              /* line ignored */
              ignore_counter--;
              }
            }
          }
        fclose(command_file);
        return true;
        }
      }
    }
  }
fclose(command_file);
information->error.number=ERROR_NO_ACTION;
information->error.number_of_parameters=2;
strcpy(information->error.parameter[0], command_tag);
strcpy(information->error.parameter[1], information->name_of_control_file);
return false;
}

bool continue_action_internal(struct information_struct *information)
{
int ignore_counter;
char input_line[MAX_STRING_LENGTH];
FILE *command_file;

if (!information) return false;
information->dialogue_flag=false;
information->subaction_code=ACTION_CODE_UNDETERMINED;
if (!(command_file=fopen(information->name_of_control_file, "r"))) {
  information->error.number=ERROR_CONTROL_FILE;
  information->error.number_of_parameters=1;
  strcpy(information->error.parameter[0], information->name_of_control_file);
  return false;
  }
fsetpos(command_file,
        &information->action_continue_position[information->action_stack_pos]);
/* line position still valid from last call */
ignore_counter=0;
while (!feof(command_file)) {
  if (get_line(command_file,
               input_line)) {
    if ((strcmp(input_line, ACTION_TAG_END) == 0) &&
         (ignore_counter >= 0)) {
      /* Action is at end */
      fclose(command_file);
      return true;
      }
    information->action_function_line[information->action_stack_pos]++;
    if (ignore_counter == 0) {
      /* execute commands */
      if (!execute_function(information,
                            input_line,
                            &ignore_counter)) {
        /* Interupt mode is not an error */
        if ((information->dialogue_flag) ||
            (information->subaction_code != ACTION_CODE_UNDETERMINED)){
          /* Note actual file position to continue */
          fgetpos(command_file,
                  &information->action_continue_position[information->action_stack_pos]);
          fclose(command_file);
          return false;
          }
        else {
          /* Error has been set by function already */
          fclose(command_file);
          return false;
          }
        }
      }
    else if (ignore_counter < 0) {
      /* Rewind to start of action */
      fsetpos(command_file,
              &information->action_start_position[information->action_stack_pos]);
      /* Calculate lines to skip */
      ignore_counter+=information->action_function_line[information->action_stack_pos] - 1;
      if (ignore_counter < 0) {
        ignore_counter=0;
        }
      information->action_function_line[information->action_stack_pos]=-1;
      }
    else {
      /* line ignored */
      ignore_counter--;
      }
    }
  }
fclose(command_file);
return true;
}

bool do_action(struct information_struct *information,
               int action,
               int sub_selection)
{
bool result;

if (information->trace) {
  fprintf(information->trace,
          "do action\n");
  }
information->action_stack_pos=0;
result=do_action_internal(information,
                          action,
                          sub_selection);
while(true) {
  if (result) {
    if (information->action_stack_pos == 0) {
      return true;
      }
    information->action_stack_pos--;
    result=continue_action_internal(information);
    }
  else {
    if ((information->subaction_code) != ACTION_CODE_UNDETERMINED) {
      information->action_stack_pos++;
      result=do_action_internal(information,
                                information->subaction_code,
                                information->sub_selection);
      }
    else {
      return false;
      }
    }
  }
}

bool continue_action(struct information_struct *information)
{
bool result;

if (information->trace) {
  fprintf(information->trace,
          "continue action\n");
  }
result=continue_action_internal(information);
while(true) {
  if (result) {
    if (information->action_stack_pos == 0) {
      return true;
      }
    information->action_stack_pos--;
    result=continue_action_internal(information);
    }
  else {
    if ((information->subaction_code) != ACTION_CODE_UNDETERMINED) {
      information->action_stack_pos++;
      result=do_action_internal(information,
                                information->subaction_code,
                                information->sub_selection);
      }
    else {
      return false;
      }
    }
  }
}

#endif
