/* RISCOS Printing System Control String Handling */
/* Created 09.08.2003 T. Milius
   Changed 26.08.2004 T. Milius */
/* (c) Copyright 2003-2004 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. */
/* ANSI-C */

#ifndef pcntrl_strings_h
#define pcntrl_strings_h

/* !!!!!!!!!! libraries !!!!!!!!!! */
/* ---------- RISCOS ---------- */
#include <kernel.h>
#include <swis.h>

/* ---------- own ---------- */
#include "stack.h"

/* !!!!!!!!!! definitions !!!!!!!!!! */
/* to clarify reading */
#ifndef FALSE
#define FALSE 0
#endif

#ifndef TRUE
#define TRUE 1
#endif

/* RISCOS printer control sequences */
#define PRINTER_STRING_SET_LINES        0
#define PRINTER_STRING_PAGE_START       1
#define PRINTER_STRING_FORM_FEED        2
#define PRINTER_STRING_PAGE_END         3
#define PRINTER_STRING_LINE_RETURN      4
#define PRINTER_STRING_LINE_SKIP        5
#define PRINTER_STRING_LINE_END_I1      6
#define PRINTER_STRING_LINE_END_I2      7
#define PRINTER_STRING_LINE_END_I3      8
#define PRINTER_STRING_ZERO_SKIP        9
#define PRINTER_STRING_LINE_START_MC_BL 10
#define PRINTER_STRING_LINE_START_MC_AL 11
#define PRINTER_STRING_LINE_PASS_C1_BL  12
#define PRINTER_STRING_LINE_PASS_C1_AL  13
#define PRINTER_STRING_LINE_PASS_C2_BL  14
#define PRINTER_STRING_LINE_PASS_C2_AL  15
#define PRINTER_STRING_LINE_PASS_C3_BL  16
#define PRINTER_STRING_LINE_PASS_C3_AL  17
#define PRINTER_STRING_LINE_PASS_C4_BL  18
#define PRINTER_STRING_LINE_PASS_C4_AL  19

/* Maximal length of control sequences inside this dumper */
#define MAX_CONTROL_SEQUENCE_LENGTH 512

/* !!!!!!!!!! data structures !!!!!!!!!! */
/* Printer control sequences */
struct command_sequence_struct {
int length;
unsigned char sequence[MAX_CONTROL_SEQUENCE_LENGTH];
};

/* !!!!!!!!!! support functions !!!!!!!!!! */

/* !!!!!!!!!! functions !!!!!!!!!! */
/* get_control_string gets a printer control sequence from
   RISCOS */
int get_control_string(unsigned char *riscos_string_base,
                       unsigned long printer_string,
                       struct command_sequence_struct *dumper_string,
                       unsigned char *extended_strings,
                       unsigned long configuration_word)
{
int i;
unsigned long *string_offset;
unsigned long *string_address;
unsigned char *string_offset_old;
unsigned char *string_length;
unsigned char *riscos_string;
_kernel_swi_regs regs;

dumper_string->length=0;
/* Attention:
   RISCOS Ltd. and Pace/Castle expanded the old printing system in a
   different way ...

   Both concepts are doing their work and are documentated. Both have
   advantages and disadvantages.

   The check for smaller than version number 5 is required by RISCOS Ltd.
   that means only if OS is 26 Bit. */
regs.r[0]=129;
regs.r[1]=0;
regs.r[2]=0xFF;
_kernel_swi(OS_Byte, &regs, &regs);
if ((regs.r[1] >= 0xAA) ||
    ((configuration_word & 0xFF000000) < 0x05000000)) {
  /* Good old but a little bit restricted RISCOS way */
  if (printer_string == PRINTER_STRING_SET_LINES) {
    string_offset_old=riscos_string_base + 33;
    }
  else if (printer_string == PRINTER_STRING_PAGE_START) {
    string_offset_old=riscos_string_base + 15;
    }
  else if (printer_string == PRINTER_STRING_FORM_FEED) {
    string_offset_old=riscos_string_base + 44;
    }
  else {
    string_offset_old=riscos_string_base + 13 + printer_string;
    }
  if (*string_offset_old == 0) {
    return FALSE;
    }
  string_length=riscos_string_base + 12 + (*string_offset_old);
  if (*string_length == 0) {
    /* Pace/Castle Extension

       If string length is 0 which makes no sense because the offset should
       be already 0 it is Pace/Castle concept.

       The offsets are pointing to blocks of 8 Byte each. There are 20 strings
       from 20 free bytes inside the classical range. So this takes
       20*8=160 bytes. This means that there is still room for
       extensions even not much. But there is the question whether an extension
       with more strings is really necessary. Each block is containing the following:

       Byte Content
       0    0 to indicate new style
       1    Length
       2    0
       3    0
       4-7  Address of string */
    string_address=(unsigned long *) string_length + 1;
    riscos_string=(unsigned char *) *string_address;
    string_length++;
    }
  else {
    riscos_string=string_length+1;
    }
  }
else {
  /* RISCOS Ltd. Extension.

     Documentation taken from official (and correct) document of RISCOS Ltd..

     R6 is pointing to an extra block which format is as follows:

     0       Number of entries in the block (currently 20)
     4       Size of block
     8       Offset from start of block to counted data1 string (n)
     12      Offset from start of block to counted data2 string (m)
     16      Offset from start of block to counted data3 string
     ...
     n       Number of chars in the data1 string
     n+1     data1 string (possibly null)
     m       Number of chars in the data2 string
     m+1     data2 string (possibly null) */
  string_offset=(unsigned long *)((unsigned char *) extended_strings + 8 + (printer_string*4));
  if (*string_offset == 0) {
    return FALSE;
    }
  string_length=extended_strings + (*string_offset);
  riscos_string=string_length+1;
  }
dumper_string->length=*string_length;
if (dumper_string->length == 0) {
  return FALSE;
  }
for (i=0; i < dumper_string->length; i++) {
  dumper_string->sequence[i]=*riscos_string;
  riscos_string++;
  }
return TRUE;
}

int copy_control_string(struct command_sequence_struct *target_string,
                        struct command_sequence_struct *source_string)
{
int i;

target_string->length=source_string->length;
for (i=0; i < source_string->length; i++) {
  target_string->sequence[i]=source_string->sequence[i];
  }
return TRUE;
}


int prepare_control_string_i(struct command_sequence_struct *target_string,
                             struct command_sequence_struct *source_string,
                             struct cellar_stack_variable_struct *environment)
{
unsigned char *old_target_start;
unsigned char *act_source_pos;
unsigned char *act_target_pos;
unsigned char *source_sequence_end;
unsigned char *stack_sequence_end;

target_string->length=0;
act_target_pos=target_string->sequence;
act_source_pos=source_string->sequence;
source_sequence_end=act_source_pos+source_string->length;
while (act_source_pos < source_sequence_end) {
  if (*act_source_pos != 0xFF) {
    if (target_string->length < MAX_CONTROL_SEQUENCE_LENGTH) {
      *act_target_pos=*act_source_pos;
      act_target_pos++;
      target_string->length++;
      }
    else {
      /* if not large enough drop all */
      return FALSE;
      }
    act_source_pos++;
    }
  else {
    /* Cellar stack sequence or char 0xFF found */
    if (act_source_pos == (source_sequence_end - 1)) {
      /* incorrect sequence so drop all */
      return FALSE;
      }
    else if (*(act_source_pos + 1) == 0xFF) {
      /* char 0xFF found */
      if (target_string->length < MAX_CONTROL_SEQUENCE_LENGTH) {
        *act_target_pos=0xFF;
        act_target_pos++;
        target_string->length++;
        }
      else {
        /* if not large enough drop all */
        return FALSE;
        }
      act_source_pos+=2;
      }
    else {
      /* Evaluate sequence */
      old_target_start=act_target_pos;
      act_source_pos++;
      stack_sequence_end=source_sequence_end;
      if ((act_target_pos=evaluate_stack(act_source_pos,
                                         &stack_sequence_end,
                                         act_target_pos,
                                         environment)) == NULL) {
        /* Problems during execution. Drop all */
        return FALSE;
        }
      else {
        act_source_pos=stack_sequence_end;
        target_string->length+=(act_target_pos - old_target_start);
        }
      }
    }
  }
return TRUE;
}

/* Prepares a printer control sequence for output to printer.
   This includes evaluation and replacement of cellar stack sequences */
int prepare_control_string(struct command_sequence_struct *target_string,
                           struct command_sequence_struct *source_string,
                           struct cellar_stack_variable_struct *environment)
{

if (prepare_control_string_i(target_string,
                             source_string,
                             environment)) {
  return TRUE;
  }
else {
  /* Problems occurred. Drop target string content. */
  target_string->length=0;
  return FALSE;
  }
}

/* Sents a printer control string to output file */
int sent_control_string(struct command_sequence_struct *dumper_string,
                        int output_file,
                        struct cellar_stack_variable_struct *environment)
{
struct command_sequence_struct tmp_string;
_kernel_swi_regs regs;

if (dumper_string->length > 0) {
  if (prepare_control_string(&tmp_string,
                             dumper_string,
                             environment)) {
    if ((environment->dumper.printer_mode == 0) ||
        (environment->dumper.printer_mode == 1)) {
      regs.r[0]=2;
      regs.r[1]=output_file;
      regs.r[2]=(int) tmp_string.sequence;
      regs.r[3]=tmp_string.length;
      _kernel_swi(OS_GBPB, &regs, &regs);
      }
    }
  else {
    return FALSE;
    }
  }
return TRUE;
}

int execute_control_string(struct command_sequence_struct *dumper_string,
                           struct cellar_stack_variable_struct *environment)
{
struct command_sequence_struct tmp_string;
_kernel_swi_regs regs;

if (dumper_string->length > 0) {
  if (prepare_control_string(&tmp_string,
                             dumper_string,
                             environment)) {
    if (tmp_string.length > 0) {
      tmp_string.sequence[tmp_string.length]='\0';
      regs.r[0]=(int) tmp_string.sequence;
      _kernel_swi(OS_CLI, &regs, &regs);
      }
    }
  else {
    return FALSE;
    }
  }
return TRUE;
}

#endif
