//////////////////////////////////////////////////////////////////
// Shell
//
// C program shell for rapid application development
// The Flying Pig!
// Started 17/7/2003
//////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////
// Includes

#include "oslib/osmodule.h"
#include "oslib/messagetrans.h"
#include "oslib/wimp.h"
#include "oslib/dragasprite.h"
#include "oslib/macros.h"
#include "oslib/osfile.h"

#include "flexlib/flex.h"

#include "Comp.h"

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

#if defined _DEBUG
#include <kernel.h>
#endif

//////////////////////////////////////////////////////////////////
// Defines

#define WORDALIGN(b) (((b) + 3) & ~3)
#define RANGE(var,min,max)   if ((var) < (min)) var = min; \
                             if ((var) > (max)) var = max;

#define MENU_XPOS_OFFSET     (-64)
/* (96 + 44 * <menu items> + 24 * <menu separators>) */
#define ICONBAR_MENU_YPOS    (96 + 44 * 2 + 24 * 0)
#define RAMTRANSMIT_SIZE     (10 * 1024)
#define FILE_LOAD_MAX        (64)
#define POLL_DELAY           (2)
#define SEND_SMALL           (LINK_SEND_MAX)
#define SEND_LARGE           (1024*20)
#define LINKFILENAME_MAX     (RAMTRANSMIT_SIZE)
#define DATAIN_CHUNK_SIZE    (1024*5)

//////////////////////////////////////////////////////////////////
// Structures

//////////////////////////////////////////////////////////////////
// Global variables

static char                   gszProgTitle[128];
static char                   gpcTemp[256];
static char                   *gpcMessages;
//static wimp_w                 gwhMain;
//static wimp_w                 gwhSave;
static wimp_w                 gwhConf;
static wimp_w                 gwhWarn;
//static wimp_i                 gihIconBarIcon;
static int                    gnMenuWins = 0;
static wimp_w                 gawhMenuWinHandle[10];
static int                    gnMenuXpos = 0;
static int                    gnMenuYpos = 0;
static wimp_menu              *gpcMenuCurrent = NULL;
static wimp_menu              *gpcIconBarMenu;
static wimp_menu              *gpcWindowMenu;
static wimp_t                 gnTaskHandle;
static bool                   gboDrag = FALSE;
static char                   gacFontRef[255];
static int                    gnMessageMyRef = 0;
//static SAVETYPE               geSaveType = SAVETYPE_INVALID;
//static LOADTYPE               geLoadType = LOADTYPE_INVALID;
static int                    gnSaveFileType;
//static char                   gszSaveString[255];
static char                   *gpcExample;
static int                    gnExampleSize;

//static wimp_message_data_xfer gsMessageStore;
//static wimp_t                 gthSender;
//static char                   gpcRamTransBuffer[RAMTRANSMIT_SIZE];
//static int                    gnMyRef;

//static int                    gnRamTransRef;
//static char *                 gpcRamTransPos;
//static int                    gnRamTransLeft;

static int                    gnLinkHandle = -1;
static wimp_t                 ghLinkTask = NULL;
static char                   gpcLinkBuffer[LINK_RAM_BUFFER];
static int                    gnLinkTransRef = -1;
static char                   *gpcLinkTransPos = NULL;
static int                    gnLinkTransLeft = 0;


static int                    gnSendSize;

//static char                   *gapcLoaded[FILE_LOAD_MAX];
//static int                    ganLoadedSize[FILE_LOAD_MAX];
//static int                    gnLoadedNum = 0;
//static int                    gnDataSend = -1;
//static char                   gszFileName[LINKFILENAME_MAX];
//static int                    gnFileNamePos;
//static bool                   gboFileNameGood;

static char                   *gapcDataOut[FILE_LOAD_MAX];
static int                    ganDataOutSize[FILE_LOAD_MAX];
static int                    gnDataOutNum;
static int                    gnDataSend = -1;

static char                   *gpcDataIn;
static int                    gnDataInSize;

static int                    gnDataInPos;


void ConfigSetUpWindow (void);
void ConfigSetFromWindow (void);
int Evaluate (char * * ppcDataOut, int * pnDataOutSize, char * * ppcDataIn, int * pnDataInSize);

//////////////////////////////////////////////////////////////////
// Main application

// Respond to swi errors
inline void err (os_error * sError)
{
  if (sError)
  {
    ShowWarning (sError);
  }
}

// Main program
int main (/*int argc, char * * argv*/)
{
  wimp_block                  cBlock;
  wimp_version_no             nVersion;
  int                         nCount;
  wimp_event_no               nEvent;
  wimp_MESSAGE_LIST(10) sMessages = {{
//                                      message_DATA_SAVE,
//                                      message_DATA_SAVE_ACK,
//                                      message_DATA_LOAD,
//                                      message_DATA_OPEN,
//                                      message_RAM_FETCH,
//                                      message_RAM_TRANSMIT,
                                      message_HELP,
                                      message_URL_LAUNCH,
                                      message_LINK_CONTROL,
                                      message_LINK_OPEN,
                                      message_LINK_CLOSE,
                                      message_LINK_SEND,
                                      message_LINK_DATASAVE,
                                      message_LINK_RAMFETCH,
                                      message_LINK_RAMTRANSMIT,
                                      0u}};
  wimp_message_list *psUserMessages = (wimp_message_list*)&sMessages;
  os_t                        nTime;

  // Load Messages file
  gpcMessages = osmodule_alloc (17 + sizeof(MESSAGES));
  strcpy(gpcMessages + 16, MESSAGES);

  messagetrans_open_file ((messagetrans_control_block*)gpcMessages,
    gpcMessages + 16, 0);

  // Initialise task
  gnTaskHandle = wimp_initialise (wimp_VERSION_RO3, Tag ("Tsk"),
    psUserMessages, & nVersion);

  // Initialise the flex memory
  strncpy (gszProgTitle, Tag("Tsk"), sizeof (gszProgTitle));
  flex_init (gszProgTitle, 0, 0);

  // Load templates
  for (nCount = 0; nCount < 256; nCount++)
  {
    gacFontRef[nCount] = 0;
  }
  wimp_open_template (TEMPLATES);
  gwhWarn = LoadTemplate ("Warning");
  gwhConf = LoadTemplate ("Config");

  // Set up general variables
  gnDataOutNum = 0;
  gnDataSend = -1;
  gnSendSize = SEND_SMALL;

  gpcDataIn = NULL;
  gnDataInSize = 0;
  gnDataInPos = -1;

  gnLinkTransRef = -1;
  gpcLinkTransPos = NULL;
  gnLinkTransLeft = 0;

  // Create menus
//  gpcIconBarMenu = CreateMenu (Tag ("Menu1"));
//  gpcWindowMenu = CreateMenu (Tag("Menu2"));

//  SetSubMenu (gpcWindowMenu, 0, (wimp_menu*)gwhSave);

  // Close templates
  wimp_close_template ();

  // Set up the version info
//  SetIconText (VERSION_STRING_LONG, gawhMenuWinHandle[0], 3);

  // Create icon
//  gihIconBarIcon = CreateIconbarIcon (Tag ("Icn"), 76, 76);

  // Main polling loop
  //////////////////////////////////////////////////////////////////
  do
  {
    nTime = os_read_monotonic_time () + POLL_DELAY;
    nEvent = wimp_poll_idle (FLAGS, & cBlock, nTime, NULL);
    switch (nEvent)
    {
      case  0: // Null
        NullPoll (& cBlock);
        break;
      case  1: // Redraw window
        break;
      case  2: // Open window
        OpenWindow (& cBlock);
        break;
      case  3: // Closewindow
        CloseWindow (& cBlock);
        break;
      case  4: // Pointer leaving window
        break;
      case  5: // Pointer entering window
        break;
      case  6:
        MouseClick (& cBlock);
        break;
      case  7: // User drag box
//        UserDragBox (& cBlock);
        break;
      case  8: // Keys
        Keys (& cBlock);
        break;
      case  9:
//        MenuSelect (& cBlock);
        break;
      case 10: // Scroll request
        break;
      case 11: // Lose caret
        break;
      case 12: // Gain caret
        break;
      case 17: // Receive message
      case 18:
        Receive (& cBlock);
        break;
      case 19: // UserMessage Acknowledge
        Acknowledged (& cBlock);
        break;
      default: // Default
        break;
    }
  } while TRUE;

  //////////////////////////////////////////////////////////////////

  return 0;
}

//////////////////////////////////////////////////////////////////
// Poll 16,17: Message received
void Receive (wimp_block *pcBlock)
{
  switch (pcBlock->message.action)
  {
    case message_QUIT:
      Quit ();
      break;
    case message_DATA_SAVE:
//      DataSave (pcBlock);
      break;
    case message_DATA_SAVE_ACK:
//      DataSaveAck (pcBlock);
      break;
    case message_DATA_LOAD:
//      DataLoad (pcBlock);
      break;
    case message_DATA_OPEN:
//      DataOpen (pcBlock);
      break;
    case message_RAM_FETCH:
//      RamFetch (pcBlock);
      break;
    case message_RAM_TRANSMIT:
//      RamTransmit (pcBlock);
      break;
    case message_HELP:
      Help (pcBlock);
      break;

    case message_LINK_CONTROL:
      ReceiveLinkControl (pcBlock);
      break;
    case message_LINK_OPEN:
      ReceiveLinkOpen (pcBlock);
      break;
    case message_LINK_CLOSE:
      ReceiveLinkClose (pcBlock);
      break;
    case message_LINK_SEND:
      ReceiveLinkSend (pcBlock);
      break;
    case message_LINK_DATASAVE:
      ReceiveLinkDataSave (pcBlock);
      break;
    case message_LINK_RAMFETCH:
      ReceiveLinkRamFetch (pcBlock);
      break;
    case message_LINK_RAMTRANSMIT:
      ReceiveLinkRamTransmit (pcBlock);
      break;
  }
}

//////////////////////////////////////////////////////////////////
// Poll 19: Message acknowledge received
void Acknowledged (wimp_block *pcBlock)
{
  switch (pcBlock->message.action)
  {
    case message_RAM_FETCH:
//      RamFetchReturned (pcBlock);
      break;
    case message_URL_LAUNCH:
      OpenURLReturned (pcBlock);
      break;
  }
  gnMessageMyRef = 0;
}

// Quit task
void Quit (void)
{
  int                         nCount;
  int                         nRemove;

  // Free the fonts
  for (nCount = 0; nCount < 256; nCount++)
  {
    if (gacFontRef[nCount])
    {
      for (nRemove = 0; nRemove < gacFontRef[nCount]; nRemove++)
      {
        xfont_lose_font ((font_f)nCount);
      }
    }
  }

  // Close the program
  wimp_close_down (gnTaskHandle);
}

//////////////////////////////////////////////////////////////////
// Poll 6: Process mouse clicks
void MouseClick (wimp_block *pcBlock)
{
//  int                         nXpos       = pcBlock->pointer.pos.x;
//  int                         nYpos       = pcBlock->pointer.pos.y;
  wimp_mouse_state            nButton     = pcBlock->pointer.buttons;
  wimp_w                      whWindow    = pcBlock->pointer.w;
  wimp_i                      ihIcon      = pcBlock->pointer.i;
//  char                        *szFileIcon;

//  if (whWindow == wimp_ICON_BAR)
//  {
//    switch (nButton)
//    {
//      case wimp_CLICK_SELECT:
//        OpenWindowInit(gwhMain);
//        break;
//      case wimp_CLICK_MENU:
//        OpenMenu (gpcIconBarMenu, nXpos + MENU_XPOS_OFFSET,
//          ICONBAR_MENU_YPOS);
//        break;
//    }
//  }
//
//  if (whWindow == gwhMain)
//  {
//    if (nButton == wimp_CLICK_MENU)
//    {
//      OpenMenu (gpcWindowMenu, nXpos - 64, nYpos);
//    }
//    else
//    {
//      switch (ihIcon)
//      {
//        default: // Save drag
//          if (nButton & (wimp_DRAG_SELECT | wimp_DRAG_ADJUST))
//          {
//            strncpy (gszSaveString, GetIconText (whWindow, ihIcon),
//              sizeof(gszSaveString));
//            DragBox (whWindow, ihIcon);
//            geSaveType = SAVETYPE_ICONTEXT;
//          }
//          break;
//      }
//    }
//  }
//
//  if (whWindow == gwhSave)
//  {
//    switch (ihIcon)
//    {
//      case (wimp_i)3: // Save drag
//        if (nButton & (wimp_DRAG_SELECT | wimp_DRAG_ADJUST))
//        {
//          szFileIcon = GetIconText (gwhSave, 3);
//          gnSaveFileType = 0xfff;
//          DragSprite (whWindow, ihIcon, "file_fff");
//          geSaveType = SAVETYPE_FILE;
//        }
//        break;
//      case (wimp_i)1: // Cancel
//        CloseMenu ();
//        break;
//      case (wimp_i)0: // Save
//        gnSaveFileType = 0xfff;
//        SaveSave ();
//        if (nButton != wimp_CLICK_ADJUST)
//        {
//          CloseMenu ();
//        }
//        break;
//    }
//  }

  if (whWindow == gwhWarn)
  {
    switch (ihIcon)
    {
      case (wimp_i)0: // OK
        CloseWindowHandle (gwhWarn);
        break;
    }
  }

  if (whWindow == gwhConf)
  {
    switch (ihIcon)
    {
      case (wimp_i)0: // Cancel
        ConfigSetUpWindow ();
        if (nButton & wimp_CLICK_SELECT)
        {
          CloseWindowHandle (gwhConf);
        }
        break;
      case (wimp_i)1: // OK
        ConfigSetFromWindow ();
        if (nButton & wimp_CLICK_SELECT)
        {
          CloseWindowHandle (gwhConf);
        }
        break;
    }
  }

//  if (whWindow == gawhMenuWinHandle[0])
//  {
//    switch (ihIcon)
//    {
//      case (wimp_i)8: // Email
//        OpenURL (Tag("Email"));
//        if (nButton != wimp_CLICK_ADJUST)
//        {
//          CloseMenu ();
//        }
//        break;
//      case (wimp_i)9: // Website
//        OpenURL (Tag("Website"));
//        if (nButton != wimp_CLICK_ADJUST)
//        {
//          CloseMenu ();
//        }
//        break;
//    }
//  }
}

//////////////////////////////////////////////////////////////////
// Poll 8: Act on user key presses
void Keys (wimp_block *pcBlock)
{
  wimp_w                      whWindow;
  wimp_i                      ihIcon;
  wimp_key_no                 nKey;

  whWindow = pcBlock->key.w;
  ihIcon = pcBlock->key.i;
  nKey = pcBlock->key.c;

  switch (nKey)
  {
    case wimp_KEY_RETURN:
//      if (whWindow == gwhSave)
//      {
//        gnSaveFileType = 0xfff;
//        SaveSave ();
//        CloseMenu ();
//      }
      if (whWindow == gwhWarn)
      {
        CloseWarning ();
      }
      if (whWindow == gwhConf)
      {
        ConfigSetFromWindow ();
        CloseWindowHandle (gwhConf);
      }
      break;
    case wimp_KEY_ESCAPE:
      if (whWindow == gwhWarn)
      {
        CloseWarning ();
      }
      if (whWindow == gwhConf)
      {
        ConfigSetUpWindow ();
        CloseWindowHandle (gwhConf);
      }
      break;
    default:
      xwimp_process_key (nKey);
      break;
  }
}

////////////////////////////////////////////////////////////////////
//// Poll 9: Act on menu selections
//void MenuSelect (wimp_block *pcBlock)
//{
//  wimp_pointer                sPointer;
//
//  xwimp_get_pointer_info (& sPointer);
//
//  if (gpcMenuCurrent == gpcIconBarMenu)
//  {
//    switch (pcBlock->selection.items[0])
//    {
//      case 1:
//        Quit ();
//        break;
//      default:
//        break;
//    }
//  }
//
//  if (gpcMenuCurrent == gpcWindowMenu)
//  {
//    switch (pcBlock->selection.items[0])
//    {
//      case 1:
//        Quit ();
//        break;
//      default:
//        break;
//    }
//  }
//
//  if (sPointer.buttons & wimp_CLICK_ADJUST)
//  {
//    OpenMenu (gpcMenuCurrent, gnMenuXpos, gnMenuYpos);
//  }
//}

////////////////////////////////////////////////////////////////////
//// Poll 7: User has finished dragging an object
//void UserDragBox (wimp_block *pcBlock)
//{
//  char                        *szFilename;
//  wimp_w                      whWindow;
//  wimp_pointer                sPointer;
//  wimp_message                sMessage;
//
//  pcBlock = pcBlock;
//
//  xdragasprite_stop ();
//  gboDrag = FALSE;
//
//  xwimp_get_pointer_info (& sPointer);
//
//  whWindow = sPointer.w;
//
//  if ((whWindow != gwhMain)
//     && (whWindow != gwhSave)
//     && (whWindow != gwhWarn))
//  {
//    switch (geSaveType)
//    {
//      case SAVETYPE_FILE:
//        sMessage.data.data_xfer.w = whWindow;
//        sMessage.data.data_xfer.i = sPointer.i;
//        sMessage.data.data_xfer.pos = sPointer.pos;
//        sMessage.data.data_xfer.est_size = gnExampleSize;
//        sMessage.data.data_xfer.file_type = gnSaveFileType;
//
//        szFilename = GetIconText (gwhSave, 2);
//        while (strchr (szFilename, '.'))
//        {
//          szFilename = strchr(szFilename, '.') + 1;
//        }
//
//        strncpy (sMessage.data.data_xfer.file_name, szFilename, 211);
//        sMessage.data.data_xfer.file_name[211] = 0;
//
//        sMessage.size = WORDALIGN((44 + 1 + strlen (szFilename)));
//        sMessage.your_ref = 0;
//        sMessage.action = 1;
//
//        xwimp_send_message_to_window (wimp_USER_MESSAGE, & sMessage,
//                   whWindow, sPointer.i, NULL);
//        break;
//      case SAVETYPE_ICONTEXT:
//        sMessage.data.data_xfer.w = whWindow;
//        sMessage.data.data_xfer.i = sPointer.i;
//        sMessage.data.data_xfer.pos = sPointer.pos;
//        sMessage.data.data_xfer.est_size = strlen(gszSaveString) + 1;
//        sMessage.data.data_xfer.file_type = 0xfff;
//
//        strncpy (sMessage.data.data_xfer.file_name, "IconText", 12);
//        sMessage.data.data_xfer.file_name[11] = 0;
//
//        sMessage.size = WORDALIGN((44 + 12));
//        sMessage.your_ref = 0;
//        sMessage.action = 1;
//
//        xwimp_send_message_to_window (wimp_USER_MESSAGE, & sMessage,
//          whWindow, sPointer.i, NULL);
//        break;
//      default:
//        break;
//    }
//  }
//}

//////////////////////////////////////////////////////////////////
// Poll 2: Open window given window block
void OpenWindow (wimp_block *pcBlock)
{
  xwimp_open_window (& pcBlock->open);
}

//////////////////////////////////////////////////////////////////
// Open a window on screen that is currently closed
void OpenWindowInit(wimp_w whWindow)
{
  wimp_window_state           sState;

  sState.w = whWindow;
  xwimp_get_window_state (& sState);
  sState.next = wimp_TOP;
  xwimp_open_window ((wimp_open *)& sState);
}

//////////////////////////////////////////////////////////////////
// Open a window in the centre of the screen
void OpenWindowInitCentre(wimp_w whWindow)
{
  int                         nHeight;
  int                         nWidth;
  int                         nEigFactor;
  int                         nSize;
  int                         nPosition;
  wimp_window_state           sState;

  sState.w = whWindow;
  xwimp_get_window_state (& sState);
  sState.next = wimp_TOP;

  nWidth = sState.visible.x1 - sState.visible.x0;
  nHeight = sState.visible.y1 - sState.visible.y0;

  xos_read_mode_variable (os_CURRENT_MODE, os_MODEVAR_XEIG_FACTOR,
    & nEigFactor, NULL);
  xos_read_mode_variable (os_CURRENT_MODE, os_MODEVAR_XWIND_LIMIT,
    & nSize, NULL);
  nPosition = ((nSize << nEigFactor) - nWidth) / 2;
  sState.visible.x0 = nPosition;
  sState.visible.x1 = nPosition + nWidth;

  xos_read_mode_variable (os_CURRENT_MODE, os_MODEVAR_YEIG_FACTOR,
    & nEigFactor, NULL);
  xos_read_mode_variable (os_CURRENT_MODE, os_MODEVAR_YWIND_LIMIT,
    & nSize, NULL);
  nPosition = ((nSize << nEigFactor) - nHeight) / 2;
  sState.visible.y0 = nPosition;
  sState.visible.y1 = nPosition + nHeight;

  xwimp_open_window ((wimp_open *)& sState);
}

//////////////////////////////////////////////////////////////////
// Open a given menu at the given position on screen
void OpenMenu (wimp_menu * pcMenu, int nXpos, int nYpos)
{
  xwimp_create_menu (pcMenu, nXpos, nYpos);

  gnMenuXpos = nXpos;
  gnMenuYpos = nYpos;
  gpcMenuCurrent = pcMenu;
}

//////////////////////////////////////////////////////////////////
// Poll 3: Close a window
void CloseWindow (wimp_block *pcBlock)
{
  wimp_close_window (pcBlock->close.w);
}

//////////////////////////////////////////////////////////////////
// Close window given handle
void CloseWindowHandle (wimp_w whWindow)
{
  wimp_close_window (whWindow);
}

//////////////////////////////////////////////////////////////////
// Close the currently open menu
void CloseMenu (void)
{
  xwimp_create_menu ((wimp_menu*)-1, 0, 0);

  gnMenuXpos = 0;
  gnMenuYpos = 0;
  gpcMenuCurrent = NULL;
}

//////////////////////////////////////////////////////////////////
// Create an iconbar icon
wimp_i CreateIconbarIcon (char * szSprite, int nWidth, int nHeight)
{
  wimp_i                      ihIcon;
  wimp_icon_create            sIconBlock;

  sIconBlock.w = wimp_ICON_BAR_RIGHT;
  sIconBlock.icon.extent.x0 = 0;
  sIconBlock.icon.extent.y0 = 0;
  sIconBlock.icon.extent.x1 = nWidth;
  sIconBlock.icon.extent.y1 = nHeight;
  sIconBlock.icon.flags = wimp_ICON_SPRITE |
                   (wimp_BUTTON_CLICK << wimp_ICON_BUTTON_TYPE_SHIFT);
  strncpy(sIconBlock.icon.data.sprite, szSprite, 12);

  ihIcon = wimp_create_icon (& sIconBlock);

  return ihIcon;
}

//////////////////////////////////////////////////////////////////
// Create window from Templates file
wimp_w LoadTemplate (char * szWindowTitle)
{
  wimp_window                 *pcWindow;
  char *                      pcIndirected;
  int                         nWindowSize;
  int                         nIndirectedSize;
  char                        szTitle[12];
  wimp_w                      whWindow;

  wimp_load_template (0, NULL, NULL, NULL, szWindowTitle, 0,
    & nWindowSize, & nIndirectedSize);

  pcWindow = (wimp_window*)malloc (nWindowSize);
  pcIndirected = malloc (nIndirectedSize);
  strncpy (szTitle, szWindowTitle, 12);

  wimp_load_template (pcWindow, pcIndirected, pcIndirected + nIndirectedSize,
    gacFontRef, szWindowTitle, 0, NULL, NULL);

  whWindow = wimp_create_window (pcWindow);

  free((void *)pcWindow);

  return whWindow;
}

//////////////////////////////////////////////////////////////////
// Create window from Templates file with sprites
wimp_w LoadTemplateSprites (char * szWindowTitle, osspriteop_area * pcSpriteArea)
{
  wimp_window                 *pcWindow;
  char *                      pcIndirected;
  int                         nWindowSize;
  int                         nIndirectedSize;
  char                        szTitle[12];
  wimp_w                      whWindow;

  wimp_load_template (0, NULL, NULL, NULL, szWindowTitle, 0,
    & nWindowSize, & nIndirectedSize);

  pcWindow = (wimp_window*)malloc (nWindowSize);
  pcIndirected = malloc (nIndirectedSize);
  strncpy (szTitle, szWindowTitle, 12);

  wimp_load_template (pcWindow, pcIndirected, pcIndirected + nIndirectedSize,
    gacFontRef, szWindowTitle, 0, NULL, NULL);

  if (pcSpriteArea)
  {
    pcWindow->sprite_area = pcSpriteArea;
  }

  whWindow = wimp_create_window (pcWindow);

  free((void *)pcWindow);

  return whWindow;
}

//////////////////////////////////////////////////////////////////
// Create a menu from a text string - usually kept in the messages file
wimp_menu * CreateMenu (char * szMenu)
{
  wimp_menu                   *pcMenu;
  char *                      pcBuffer;
  char *                      nAt = 0;
  char *                      nAt2 = 0;
  int                         nWidth = 0;
  wimp_menu_flags             uFlags;
  char                        szMenuString[512];
  char *                      pcMenuString;
  int                         nMenuItems;
  char                        *szItem;
  int                         nMenuEntry;
  int                         nStringLen;

  nMenuItems = -1;
  szItem = szMenu;
  while (szItem)
  {
    nMenuItems++;
    szItem = strchr(szItem + 1, ',');
  }

  pcMenu = (wimp_menu*)malloc (wimp_SIZEOF_MENU(nMenuItems));
  pcBuffer = (char *)malloc (strlen(szMenu));

  nAt = strchr (szMenu, ',');
  if (!nAt)
  {
    nAt = (char *)strlen (szMenu);
  }
  else
  {
    nAt -= (int)szMenu;
  }
  strncpy (szMenuString, szMenu, (int)nAt);
  szMenuString[(int)nAt] = 0;

  if (strlen (szMenuString) >= 12)
  {
    strcpy (pcBuffer, szMenuString);
    pcMenu->title_data.indirected_text.text = pcBuffer;
    pcBuffer += strlen (szMenuString) + 1;
    uFlags = wimp_MENU_TITLE_INDIRECTED;
  }
  else
  {
    strncpy (pcMenu->title_data.text, szMenuString, 12);
    uFlags = 0u;
  }
  pcMenu->title_fg = wimp_COLOUR_BLACK;
  pcMenu->title_bg = wimp_COLOUR_LIGHT_GREY;
  pcMenu->work_fg = wimp_COLOUR_BLACK;
  pcMenu->work_bg = wimp_COLOUR_WHITE;

  pcMenu->height = 44;
  pcMenu->gap = 0;

  nMenuEntry = 0;
  do
  {
    pcMenu->entries[nMenuEntry].sub_menu = (wimp_menu*)-1;
    pcMenu->entries[nMenuEntry].icon_flags = wimp_ICON_TEXT
                   | wimp_ICON_FILLED
                   | (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT);
    nAt2 = nAt + 1;

    nAt = strchr (szMenu + (int)nAt2, ',');
    if (!nAt)
    {
      nAt = (char *)strlen (szMenu) + 1;
    }
    else
    {
      nAt -= (int)szMenu;
    }
    strncpy (szMenuString, szMenu + (int)nAt2, (int)nAt - (int)nAt2);
    szMenuString[(int)nAt - (int)nAt2] = 0;

    pcMenuString = szMenuString;

    if (pcMenuString[0] == '+')
    {
      uFlags = wimp_MENU_TICKED;
      pcMenuString++;
    }
    if (pcMenuString[0] == '-')
    {
      uFlags |= wimp_MENU_SEPARATE;
      pcMenuString++;
    }
    if (pcMenuString[0] == '|')
    {
      uFlags |= wimp_MENU_WRITABLE;
      pcMenuString++;
    }
    if (pcMenuString[0] == '>')
    {
      pcMenuString++;
      if (gnMenuWins < (int)sizeof(gawhMenuWinHandle))
      {
        gawhMenuWinHandle[gnMenuWins] = LoadTemplate (pcMenuString);
        pcMenu->entries[nMenuEntry].sub_menu
                   = (wimp_menu*)gawhMenuWinHandle[gnMenuWins];

        gnMenuWins++;
      }
    }

    nStringLen = (int)strlen (pcMenuString);
    if (strlen (pcMenuString) >= 12)
    {
      pcMenu->entries[nMenuEntry].icon_flags |= wimp_ICON_INDIRECTED;
      pcMenu->entries[nMenuEntry].data.indirected_text.text = pcBuffer;
      pcMenu->entries[nMenuEntry].data.indirected_text.validation = NULL;
      pcMenu->entries[nMenuEntry].data.indirected_text.size = nStringLen + 1;
      strcpy (pcBuffer, pcMenuString);
      pcBuffer += nStringLen + 1;
    }
    else
    {
      strncpy (pcMenu->entries[nMenuEntry].data.text, pcMenuString, 12);
    }

    pcMenu->entries[nMenuEntry].menu_flags = uFlags;
    if (nStringLen > nWidth)
    {
      nWidth = nStringLen;
    }
    nMenuEntry++;
    uFlags = 0u;
  } while ((int)nAt < (int)strlen (szMenu));

  pcMenu->width = (nWidth + 1) * 16;
  pcMenu->entries[nMenuEntry - 1].menu_flags |= wimp_MENU_LAST;

  return pcMenu;
}

//////////////////////////////////////////////////////////////////
// Attach a submenu to a menu
void SetSubMenu (wimp_menu * pcMain, int nItem, wimp_menu * pcSub)
{
  pcMain->entries[nItem].sub_menu = pcSub;
}

//////////////////////////////////////////////////////////////////
// Set the greyed out status of a menu item
void SetMenuItemGreyness (bool boState, wimp_menu * pcMenu, int nItem)
{
  if (boState)
  {
    pcMenu->entries[nItem].icon_flags |= wimp_ICON_SHADED;
  }
  else
  {
    pcMenu->entries[nItem].icon_flags &= ~wimp_ICON_SHADED;
  }
}

//////////////////////////////////////////////////////////////////
// Set the ticked status of a menu item
void SetMenuItemTicked (bool boTicked, wimp_menu * pcMenu, int nItem)
{
  if (boTicked)
  {
    pcMenu->entries[nItem].menu_flags |= wimp_MENU_TICKED;
  }
  else
  {
    pcMenu->entries[nItem].menu_flags &= ~wimp_MENU_TICKED;
  }
}

//////////////////////////////////////////////////////////////////
// Get the text string from an icon
char * GetIconText (wimp_w whWindow, wimp_i ihIcon)
{
  wimp_icon_state             sIconState;
  char                        *szString;
  int                         nTerminate = 0;

  sIconState.w = whWindow;
  sIconState.i = ihIcon;
  xwimp_get_icon_state (& sIconState);

  if (sIconState.icon.flags & wimp_ICON_INDIRECTED)
  {
    szString = sIconState.icon.data.indirected_text.text;
    while (szString[nTerminate] >= 32)
    {
      nTerminate++;
    }
    szString[nTerminate] = 0;
  }
  else
  {
    szString = sIconState.icon.data.text;
    while ((szString[nTerminate] >= 32) && (nTerminate < 12))
    {
      nTerminate++;
    }
    szString[nTerminate] = 0;
  }
  return szString;
}

//////////////////////////////////////////////////////////////////
// Set the text string for an icon
void SetIconText (char * szText, wimp_w whWindow, wimp_i ihIcon)
{
  wimp_icon_state             sIconState;

  sIconState.w = whWindow;
  sIconState.i = ihIcon;
  xwimp_get_icon_state (& sIconState);

  if (sIconState.icon.flags & wimp_ICON_INDIRECTED)
  {
    strcpy (sIconState.icon.data.indirected_text.text, szText);
  }
  else
  {
    strncpy (sIconState.icon.data.text, szText, 12);
    sIconState.icon.data.text[11] = 0;
  }

  xwimp_set_icon_state (whWindow, ihIcon, 0, 0);
}

//////////////////////////////////////////////////////////////////
// Get the selection state from an icon
bool GetIconSelectionState (wimp_w whWindow, wimp_i ihIcon)
{
  wimp_icon_state             sIconState;
  bool                        boState;

  sIconState.w = whWindow;
  sIconState.i = ihIcon;
  xwimp_get_icon_state (& sIconState);

  if (sIconState.icon.flags & wimp_ICON_SELECTED)
  {
    boState = TRUE;
  }
  else
  {
    boState = FALSE;
  }

  return boState;
}

//////////////////////////////////////////////////////////////////
// Set the selection state from an icon
void SetIconSelectionState (bool boState, wimp_w whWindow, wimp_i ihIcon)
{
  wimp_icon_flags             uFlagsEOR;

  if (boState)
  {
    uFlagsEOR = wimp_ICON_SELECTED;
  }
  else
  {
    uFlagsEOR = 0;
  }

  xwimp_set_icon_state (whWindow, ihIcon, uFlagsEOR, wimp_ICON_SELECTED);
}

//////////////////////////////////////////////////////////////////
// Grey out or ungrey an icon
void SetIconGreyness (bool boState, wimp_w whWindow, wimp_i ihIcon)
{
  wimp_icon_flags             uFlagsEOR;

  if (boState)
  {
    uFlagsEOR = wimp_ICON_SHADED;
  }
  else
  {
    uFlagsEOR = 0;
  }

  xwimp_set_icon_state (whWindow, ihIcon, uFlagsEOR, wimp_ICON_SHADED);
}

////////////////////////////////////////////////////////////////////
//// Sender wants to send data to the receiver
//// Normal use: user has terminated a drag, so this message is sent
//// Response:   DataSaveAck
////             RamFetch
//void DataSave (wimp_block *pcBlock)
//{
//  // Check filetype
//  if (pcBlock->message.data.data_xfer.file_type == 0xfff)
//  {
//    // Make a copy in case the RamFetch returns;
//    gsMessageStore = pcBlock->message.data.data_xfer;
//    gthSender = pcBlock->message.sender;
//
//    pcBlock->message.size = 28;
//    pcBlock->message.your_ref = pcBlock->message.my_ref;
//    pcBlock->message.action = message_RAM_FETCH;
//    pcBlock->message.data.ram_xfer.addr = gpcRamTransBuffer;
//    pcBlock->message.data.ram_xfer.size = RAMTRANSMIT_SIZE;
//
//    xwimp_send_message (wimp_USER_MESSAGE_RECORDED, & pcBlock->message,
//      pcBlock->message.sender);
//
//    gnMyRef = pcBlock->message.my_ref;
//    geLoadType = LOADTYPE_FILE;
//
//    // We're going to send this data on, so we need to open a link
//    LinkOpen (0);
//  }
//}
//
////////////////////////////////////////////////////////////////////
//// RamFetch Acknowledge received - try DataSaveAck instead
//// Usually followed by a DataLoad message
//void RamFetchReturned (wimp_block *pcBlock)
//{
//  if (gnMyRef == pcBlock->message.my_ref)
//  {
//    // Use the copy made earlier during the DataSave
//    pcBlock->message.data.data_xfer = gsMessageStore;
//
//    pcBlock->message.size = 60;
//    pcBlock->message.your_ref = pcBlock->message.my_ref;
//    pcBlock->message.action = message_DATA_SAVE_ACK;
//    strcpy (pcBlock->message.data.data_xfer.file_name, "<Wimp$Scrap>");
//
//    xwimp_send_message (wimp_USER_MESSAGE, & pcBlock->message,
//      gthSender);
//  }
//}
//
////////////////////////////////////////////////////////////////////
//// Acknowledge that you received a DataSave message
//// Usually followed by a DataLoad message
//void DataSaveAck (wimp_block *pcBlock)
//{
//  char                        *szFilename;
//  int                         nStringLen;
//
//  CloseMenu ();
//
//  switch (geSaveType)
//  {
//      case SAVETYPE_FILE:
//        szFilename = pcBlock->message.data.data_xfer.file_name;
//        // Save the file to the given filename
//        SetIconText (szFilename, gwhSave, 2);
//
//        // Save the file
//        SaveFile (szFilename);
//
//        pcBlock->message.your_ref = pcBlock->message.my_ref;
//        pcBlock->message.action = message_DATA_LOAD;
//
//        xwimp_send_message (wimp_USER_MESSAGE, & pcBlock->message,
//          pcBlock->message.sender);
//        break;
//      case SAVETYPE_ICONTEXT:
//        szFilename = pcBlock->message.data.data_xfer.file_name;
//        // Save the file to the given filename
//        nStringLen = strlen (gszSaveString);
//
//        err (xosfile_save_stamped (szFilename, 0xfff, gszSaveString,
//          (gszSaveString + nStringLen)));
//
//        pcBlock->message.your_ref = pcBlock->message.my_ref;
//        pcBlock->message.action = message_DATA_LOAD;
//
//        xwimp_send_message (wimp_USER_MESSAGE, & pcBlock->message,
//          pcBlock->message.sender);
//        break;
//      default:
//        break;
//  }
//}
//
////////////////////////////////////////////////////////////////////
//// Receiver of this message should load the file
//// and answer with DataSaveAck if successful
//void DataLoad (wimp_block *pcBlock)
//{
//  char                        *szFilename;
////  char                        *pcLoadedFile;
////  int                         nSize;
//
//  // Check filetype
//  if (pcBlock->message.data.data_xfer.file_type == 0xfff)
//  {
//    szFilename = pcBlock->message.data.data_xfer.file_name;
//
//    // Load the file from the given filename
//    LoadFileFlex (szFilename, & pcLoadedFile, & nSize);
//
//    // Delete the file
//    if (strcmp (szFilename, "<Wimp$Scrap>") == 0)
//    {
//      err (xosfile_delete (szFilename, NULL, NULL, NULL, NULL, NULL));
//    }
//
//    if (pcLoadedFile)
//    {
//      pcBlock->message.your_ref = pcBlock->message.my_ref;
//      pcBlock->message.action = message_DATA_LOAD_ACK;
//
//      xwimp_send_message (wimp_USER_MESSAGE, & pcBlock->message,
//        pcBlock->message.sender);
//
//      FileLoadedFlex (& pcLoadedFile, nSize);
//
//      // Now we're done with the file
//      if (pcLoadedFile)
//      {
//        flex_free ((flex_ptr)(& pcLoadedFile));
//        pcLoadedFile = NULL;
//      }
//    }
//  }
//}
//
////////////////////////////////////////////////////////////////////
//// User has double clicked on a file
//void DataOpen (wimp_block *pcBlock)
//{
//  char                        *szFilename;
//  char                        *pcLoadedFile;
//  int                         nSize;
//
//  // Check filetype
//  if (pcBlock->message.data.data_xfer.file_type == 0xffd)
//  {
//    szFilename = pcBlock->message.data.data_xfer.file_name;
//
//    // Load the file from the given filename
//    LoadFileFlex (szFilename, & pcLoadedFile, & nSize);
//
//    if (pcLoadedFile)
//    {
//      pcBlock->message.your_ref = pcBlock->message.my_ref;
//      pcBlock->message.action = message_DATA_LOAD_ACK;
//
//      xwimp_send_message (wimp_USER_MESSAGE, & pcBlock->message,
//        pcBlock->message.sender);
//
//      FileLoadedFlex (& pcLoadedFile, nSize);
//
//      // Now we're done with the file
//      if (pcLoadedFile)
//      {
//        flex_free ((flex_ptr)(& pcLoadedFile));
//        pcLoadedFile = NULL;
//      }
//    }
//  }
//}
//
////////////////////////////////////////////////////////////////////
//// Copy the data from here to the external program
//// Reply with RamTransmit
//void RamFetch (wimp_block *pcBlock)
//{
//  wimp_t                      nDestHandle;
//  int                         nSize;
//  int                         nSendLen;
//
//  nSize = pcBlock->message.data.ram_xfer.size;
//  nDestHandle = pcBlock->message.sender;
//
//  if (gnRamTransRef != pcBlock->message.your_ref)
//  {
//    // First in a series of RAM transfers
//    CloseMenu ();
//
//    switch (geSaveType)
//    {
//      case SAVETYPE_FILE:
//        // Save out the example file
//        gpcRamTransPos = gpcExample;
//        gnRamTransLeft = gnExampleSize;
//        gnRamTransRef = pcBlock->message.your_ref;
//        break;
//      case SAVETYPE_ICONTEXT:
//        gpcRamTransPos = gszSaveString;
//        gnRamTransLeft = (int)strlen (gszSaveString);
//        gnRamTransRef = pcBlock->message.your_ref;
//        break;
//      default:
//        gnRamTransRef = -1;
//        break;
//    }
//  }
//
//  if ((gnRamTransRef == pcBlock->message.your_ref) && (gpcRamTransPos))
//  {
//    // Continue with a previously started RAM transfer
//    // (it might have been started just a few lines of code before this!)
//    if (gnRamTransLeft < nSize)
//    {
//      // This will be the last transfer
//      nSendLen = gnRamTransLeft;
//    }
//    else
//    {
//      // There will be more to transfer after this
//      nSendLen = nSize;
//    }
//
//    if (nSendLen > 0)
//    {
//      xwimp_transfer_block (gnTaskHandle, gpcRamTransPos, nDestHandle,
//        pcBlock->message.data.ram_xfer.addr, nSendLen);
//    }
//
//    pcBlock->message.size = 28;
//    pcBlock->message.your_ref = pcBlock->message.my_ref;
//    pcBlock->message.action = message_RAM_TRANSMIT;
//    pcBlock->message.data.ram_xfer.size = nSendLen;
//
//    xwimp_send_message (wimp_USER_MESSAGE, & pcBlock->message,
//      pcBlock->message.sender);
//
//    gpcRamTransPos += nSendLen;
//    gnRamTransLeft -= nSendLen;
//
//    if (nSendLen < nSize)
//    {
//      gnRamTransRef = -1;
//    }
//    else
//    {
//      gnRamTransRef = pcBlock->message.my_ref;
//    }
//  }
//}
//
////////////////////////////////////////////////////////////////////
//// Response to a RamFetch
//void RamTransmit (wimp_block *pcBlock)
//{
//  char *                      pcLoadedFile;
//  int                         nSize;
//  int                         nSuccess = 1;
//
//  if (gnMyRef == pcBlock->message.your_ref)
//  {
//    pcLoadedFile = pcBlock->message.data.ram_xfer.addr;
//    nSize = pcBlock->message.data.ram_xfer.size;
//
//    if (nSize != 0)
//    {
//      switch (geLoadType)
//      {
//        case LOADTYPE_FILE:
//          if (gpcLoadedFile == NULL)
//          {
//            // The first transfer
//            // Create some flex memory
//            nSuccess = flex_alloc ((flex_ptr)(& gpcLoadedFile), nSize);
//            if (nSuccess == 1)
//            {
//              // Copy the data
//              memcpy (gpcLoadedFile, pcLoadedFile, nSize);
//              gnLoadedFileSize = nSize;
//            }
//          }
//          else
//          {
//            // A continuation of the transfer
//            // Extend the flex memory
//            nSuccess = flex_extend ((flex_ptr)(& gpcLoadedFile),
//              gnLoadedFileSize + nSize);
//            if (nSuccess == 1)
//            {
//              // Copy the data
//              memcpy (gpcLoadedFile + gnLoadedFileSize, pcLoadedFile, nSize);
//              gnLoadedFileSize += nSize;
//            }
//            else
//            {
//              // Something went wrong, so we'd better just free up the
//              // memory
//              flex_free ((flex_ptr)(& gpcLoadedFile));
//              gpcLoadedFile = NULL;
//              gnLoadedFileSize = 0;
//            }
//          }
//
//          if ((nSize < RAMTRANSMIT_SIZE) && (nSuccess == 1))
//          {
//            // The last transfer
//            // Save the file out, for the sake of the example
//            FileLoadedFlex (& gpcLoadedFile, gnLoadedFileSize);
//
//            // Now we're done with the file
//            if (gpcLoadedFile)
//            {
//              flex_free ((flex_ptr)(& gpcLoadedFile));
//              gpcLoadedFile = NULL;
//            }
//            gnLoadedFileSize = 0;
//          }
//        default:
//          break;
//      }
//    }
//
//    if (nSuccess == 1)
//    {
//      if (nSize == RAMTRANSMIT_SIZE)
//      {
//        // The buffer was full, so we must ask for some more
//        pcBlock->message.size = 28;
//        pcBlock->message.your_ref = pcBlock->message.my_ref;
//        pcBlock->message.action = message_RAM_FETCH;
//        pcBlock->message.data.ram_xfer.addr = gpcRamTransBuffer;
//        pcBlock->message.data.ram_xfer.size = RAMTRANSMIT_SIZE;
//
//        xwimp_send_message (wimp_USER_MESSAGE, & pcBlock->message,
//          pcBlock->message.sender);
//
//        gnMyRef = pcBlock->message.my_ref;
//      }
//      else
//      {
//        // This was the final transfer
//        geLoadType = LOADTYPE_INVALID;
//      }
//    }
//    else
//    {
//      // There was a problem allocating memory
//      ShowWarningTag ("Er2");
//      geLoadType = LOADTYPE_INVALID;
//    }
//  }
//}

//////////////////////////////////////////////////////////////////
// Response to a Help message
void Help (wimp_block *pcBlock)
{
  wimp_w                      whWindow;
  wimp_i                      ihIcon;
  char                        szHelpText[236];
  int                         nStrLen;
  char                        szTag[256];
  int                         nWinNum = 0;
  wimp_selection              sSelection;
  bool                        boGeneral;

  whWindow = *(wimp_w*)(pcBlock->message.data.reserved + 12);
  ihIcon = *(wimp_i*)(pcBlock->message.data.reserved + 16);

  // Warning window
  if (whWindow == gwhWarn)
  {
    nWinNum = 3;
  }

  if (nWinNum)
  {
    // Window
    szHelpText[0] = 0;
    sprintf (szTag, "H%dI%d", nWinNum, ihIcon);
    strncpy (szHelpText, TagCheck (szTag), sizeof(szHelpText));
    nStrLen = strlen(szHelpText);
    if (nStrLen == 0)
    {
      sprintf (szTag, "H%d", nWinNum);
      strncpy (szHelpText, TagCheck (szTag), sizeof(szHelpText));
    }
    szHelpText[sizeof(szHelpText) - 1] = 0;
    nWinNum = -1;
  }
  else
  {
    // Menu
    wimp_get_menu_state (wimp_GIVEN_WINDOW_AND_ICON, & sSelection,
      whWindow, ihIcon);

    boGeneral = FALSE;
    if (sSelection.items[0] != -1)
    {
      // Iconbar menu
      if (gpcMenuCurrent == gpcIconBarMenu)
      {
        nWinNum = 6;
      }

      // Main window menu
      if (gpcMenuCurrent == gpcWindowMenu)
      {
        nWinNum = 7;
      }
    }
    if (nWinNum)
    {
      if (boGeneral)
      {
        sprintf (szTag, "H%d", nWinNum);
      }
      else
      {
        if (sSelection.items[1] != -1)
        {
          sprintf (szTag, "H%dI%dI%d", nWinNum, sSelection.items[0],
            sSelection.items[1]);
        }
        else
        {
          sprintf (szTag, "H%dI%d", nWinNum, sSelection.items[0]);
        }
      }

      strncpy (szHelpText, TagCheck (szTag), sizeof(szHelpText));
      nWinNum = -1;
    }
  }

  if (nWinNum == -1)
  {
    pcBlock->message.size = WORDALIGN((24 + strlen(szHelpText)));
    pcBlock->message.your_ref = pcBlock->message.my_ref;
    pcBlock->message.action = 0x503;
    strncpy (pcBlock->message.data.reserved, szHelpText, sizeof(szHelpText));

    xwimp_send_message (wimp_USER_MESSAGE, & pcBlock->message,
      pcBlock->message.sender);
  }
}

//////////////////////////////////////////////////////////////////
// Obtain string from Messages file
char * Tag (char * szTag)
{
  messagetrans_lookup ((messagetrans_control_block*)gpcMessages,
    szTag, gpcTemp, 256, 0, 0, 0, 0, NULL);

  return gpcTemp;
}

//////////////////////////////////////////////////////////////////
// Obtain string from Messages file if it exists
char * TagCheck (char * szTag)
{
  osbool                      boMore = FALSE;
  int                         nUsed = 0;
  char                        *szResult;
  os_error                    *psError;

  xmessagetrans_enumerate_tokens ((messagetrans_control_block*)gpcMessages,
    szTag, gpcTemp, 256, 0, & boMore, & nUsed, NULL);

  if (!boMore)
  {
    strcpy (gpcTemp, "");
    szResult = gpcTemp;
  }
  else
  {
    psError = xmessagetrans_lookup ((messagetrans_control_block*)gpcMessages,
      szTag, gpcTemp, 256, NULL, NULL, NULL, NULL, & szResult, & nUsed);

    if (psError)
    {
      strcpy (gpcTemp, "");
      szResult = gpcTemp;
    }
  }

  return szResult;
}

//////////////////////////////////////////////////////////////////
// Load a file into a block of memory
char * LoadFile (char * szFilename, int * pnSize)
{
  char                        *pcMemory;
  int                         nSize;
  os_error                    *psError;
  fileswitch_object_type      eObjectType;

  xosfile_read_stamped_no_path (szFilename, & eObjectType, NULL, NULL,
    & nSize, NULL, NULL);

  if ((eObjectType != fileswitch_NOT_FOUND) && (nSize > 0))
  {
    if (pnSize)
    {
      *pnSize = nSize;
    }
    pcMemory = malloc (nSize);

    if (pcMemory)
    {
      psError = xosfile_load_stamped_no_path (szFilename, pcMemory, NULL,
        NULL, NULL, NULL, NULL);

      if (psError)
      {
        free (pcMemory);
        pcMemory = NULL;
        err (psError);
      }
    }
    else
    {
      ShowWarningTag ("Er3");
    }
  }
  else
  {
    pcMemory = NULL;
  }

  return pcMemory;
}

////////////////////////////////////////////////////////////////////
//// Load a file into a block of flex memory
//int LoadFileFlex (char * szFilename, char * * ppcMemory, int * pnSize)
//{
//  int                         nSize;
//  int                         nSuccess = 0;
//  fileswitch_object_type      eObjectType;
//  os_error                    *psError;
//
//  xosfile_read_stamped_no_path (szFilename, & eObjectType, NULL, NULL,
//    & nSize, NULL, NULL);
//
//  if ((eObjectType != fileswitch_NOT_FOUND) && (nSize > 0))
//  {
//    if (pnSize)
//    {
//      *pnSize = nSize;
//    }
//    nSuccess = flex_alloc ((flex_ptr) ppcMemory, nSize);
//
//    if ((nSuccess == 1) && (*ppcMemory))
//    {
//      psError = xosfile_load_stamped_no_path (szFilename, * ppcMemory, NULL,
//        NULL, NULL, NULL, NULL);
//
//      if (psError)
//      {
//        flex_free ((flex_ptr) ppcMemory);
//        *ppcMemory = NULL;
//        nSuccess = -1;
//        err (psError);
//      }
//    }
//    else
//    {
//      ShowWarningTag ("Er3");
//      nSuccess = 0;
//      *ppcMemory = NULL;
//    }
//  }
//  else
//  {
//    nSuccess = -1;
//    *ppcMemory = NULL;
//  }
//
//  return nSuccess;
//}

//////////////////////////////////////////////////////////////////
// Load a sprite file into a block of memory
osspriteop_area * LoadSprites (char * szFilename)
{
  osspriteop_area             *pcMemory;
  int                         nSize;

  xosfile_read_stamped_no_path (szFilename, NULL, NULL, NULL, & nSize,
    NULL, NULL);

  if (nSize > 0)
  {
    pcMemory = (osspriteop_area*)malloc (nSize);

    if (pcMemory)
    {
      pcMemory->size = nSize;
      pcMemory->sprite_count = 0;
      pcMemory->first = 16;
      pcMemory->used = 16;
      xosspriteop_clear_sprites (osspriteop_USER_AREA, pcMemory);
      err (xosspriteop_load_sprite_file (osspriteop_USER_AREA, pcMemory,
        szFilename));
    }
  }
  else
  {
    pcMemory = NULL;
  }

  return pcMemory;
}

//////////////////////////////////////////////////////////////////
// Display an error from the Messages file in a warning box
void ShowWarningTag (char * szTag)
{
    xos_bell  ();
    SetIconText (Tag(szTag), gwhWarn, 1);
    OpenWindowInitCentre (gwhWarn);
    CloseMenu ();

    xwimp_set_caret_position (gwhWarn, wimp_ICON_WINDOW, 0, 0, -1, -1);
}

//////////////////////////////////////////////////////////////////
// Display an error in a warning box
void ShowWarning (os_error * sError)
{
    xos_bell  ();
    SetIconText (sError->errmess, gwhWarn, 1);
    OpenWindowInitCentre (gwhWarn);
    CloseMenu ();

    xwimp_set_caret_position (gwhWarn, wimp_ICON_WINDOW, 0, 0, -1, -1);
}

//////////////////////////////////////////////////////////////////
// Close the warning message box
void CloseWarning (void)
{
  CloseWindowHandle (gwhWarn);
}

//////////////////////////////////////////////////////////////////
// Initiate a sprite drag from the contents of the given icon
void DragSprite (wimp_w whWindow, wimp_i ihIcon, char * szIconName)
{
  osspriteop_area             *pcSpriteArea;
  int                         nXinc;
  int                         nYinc;
  wimp_icon_state             sIconState;
  wimp_window_info            sWindowInfo;

  if (!gboDrag)
  {
    sIconState.w = whWindow;
    sIconState.i = ihIcon;
    xwimp_get_icon_state (& sIconState);

    if (((sIconState.icon.flags & (wimp_ICON_TEXT
      | wimp_ICON_SPRITE | wimp_ICON_INDIRECTED))
      == (wimp_ICON_SPRITE | wimp_ICON_INDIRECTED)))
    {
      pcSpriteArea = sIconState.icon.data.indirected_sprite.area;
    }
    else
    {
      pcSpriteArea = NULL;
    }

    sWindowInfo.w = whWindow;
    xwimp_get_window_info_header_only (& sWindowInfo);

    if (!pcSpriteArea)
    {
      pcSpriteArea = sWindowInfo.sprite_area;
    }

    nXinc = sWindowInfo.visible.x0 - sWindowInfo.xscroll;
    nYinc = sWindowInfo.visible.y1 - sWindowInfo.yscroll;

    sIconState.icon.extent.x0 += nXinc;
    sIconState.icon.extent.y0 += nYinc;
    sIconState.icon.extent.x1 += nXinc;
    sIconState.icon.extent.y1 += nYinc;

    xdragasprite_start ((dragasprite_HPOS_CENTRE | dragasprite_VPOS_CENTRE
      | dragasprite_BOUND_POINTER | dragasprite_DROP_SHADOW),
      pcSpriteArea, szIconName, & sIconState.icon.extent, NULL);

    gboDrag = TRUE;
  }
}

//////////////////////////////////////////////////////////////////
// Initiate a outline drag from the dimensions of the given icon
void DragBox (wimp_w whWindow, wimp_i ihIcon)
{
  int                         nXinc;
  int                         nYinc;
  wimp_icon_state             sIconState;
  wimp_window_state           sWindowState;
  wimp_drag                   sDragInfo;

  if (!gboDrag)
  {
    sIconState.w = whWindow;
    sIconState.i = ihIcon;
    xwimp_get_icon_state (& sIconState);

    sWindowState.w = whWindow;
    xwimp_get_window_state (& sWindowState);

    nXinc = sWindowState.visible.x0 - sWindowState.xscroll;
    nYinc = sWindowState.visible.y1 - sWindowState.yscroll;

    sDragInfo.w = 0;
    sDragInfo.type = wimp_DRAG_USER_FIXED;
    sDragInfo.initial.x0 = sIconState.icon.extent.x0 + nXinc;
    sDragInfo.initial.y0 = sIconState.icon.extent.y0 + nYinc;
    sDragInfo.initial.x1 = sIconState.icon.extent.x1 + nXinc;
    sDragInfo.initial.y1 = sIconState.icon.extent.y1 + nYinc;
    sDragInfo.bbox.x0 = 0;
    sDragInfo.bbox.y0 = 0;
    sDragInfo.bbox.x1 = 0;
    sDragInfo.bbox.y1 = 0;
    sDragInfo.handle = NULL;
    sDragInfo.draw = NULL;
    sDragInfo.undraw = NULL;
    sDragInfo.redraw = NULL;

    xwimp_drag_box (& sDragInfo);

    gboDrag = TRUE;
  }
}

//////////////////////////////////////////////////////////////////
// Initiate a URL link
void OpenURL (char * szURL)
{
  wimp_message                sMessage;

  sMessage.your_ref = 0;
  sMessage.action = 0x4af80;

  strcpy ((char *)sMessage.data.reserved, szURL);

  sMessage.size = WORDALIGN(strlen(szURL) + 21);

  xwimp_send_message (wimp_USER_MESSAGE_RECORDED, & sMessage,
    wimp_BROADCAST);

  gnMessageMyRef = sMessage.my_ref;
}

//////////////////////////////////////////////////////////////////
// No URL lancher responded, so we have to run one ourselves
void OpenURLReturned (wimp_block *pcBlock)
{
  char                        szAlias[255];
  char                        szVar[255];
  int                         nUsed;

  if (gnMessageMyRef == pcBlock->message.my_ref)
  {
    if (strncmp (pcBlock->message.data.reserved, "mailto:", 7) == 0)
    {
      strcpy (szAlias, "URLOpen_MailTo");
    }
    else
    {
      strcpy (szAlias, "URLOpen_HTTP");
    }

    sprintf (szVar, "Alias$%s", szAlias);
    xos_read_var_val (szVar, 0, -1, 0, os_VARTYPE_STRING, & nUsed,
      NULL, NULL);

    if (nUsed)
    {
      sprintf (szVar, "%s %s", szAlias, pcBlock->message.data.reserved);
      err (xwimp_start_task (szVar, NULL));
    }
  }
}

//// Save box save button clicked
//void SaveSave (void)
//{
//  char                        *szFilename;
//
//  szFilename = GetIconText (gwhSave, 2);
//
//  if (strchr(szFilename, '.'))
//  {
//    // Save the file
//    SaveFile (szFilename);
//  }
//  else
//  {
//    ShowWarningTag ("Er1");
//  }
//}

#if defined _DEBUG
//////////////////////////////////////////////////////////////////
// Report a debug message
void Report (char * szMessage)
{
  _kernel_swi_regs            sRegs;

  sRegs.r[0] = (int)szMessage;
  _kernel_swi (Report_Text0, & sRegs, & sRegs);
}

//////////////////////////////////////////////////////////////////
// Report a debug message with variable
void ReportVar (char * szFormat, int nVariable)
{
  _kernel_swi_regs            sRegs;
  char                        szReport[255];

  sprintf (szReport, szFormat, nVariable);
  sRegs.r[0] = (int)szReport;
  _kernel_swi (Report_Text0, & sRegs, & sRegs);
}
#endif // _DEBUG

//////////////////////////////////////////////////////////////////
// Save file routine - saves the example file out
void SaveFile (char * szFilename)
{
  err (xosfile_save_stamped (szFilename, gnSaveFileType,  gpcExample,
    (gpcExample + gnExampleSize)));
}

//////////////////////////////////////////////////////////////////
// Receive message LINK_CONTROL
void ReceiveLinkControl (wimp_block *pcBlock)
{
  int                         nAction;
  int                         nIdentifier;
  MemFile                     *psConfigFile;
  char                        szSection[256];
  char                        *szFilename;
  int                         nSection;

  nAction = ((int *)(pcBlock->message.data.reserved))[1];

  switch (nAction)
  {
    case 0: // Init
      gnLinkHandle = ((int *)(pcBlock->message.data.reserved))[0];
      ghLinkTask = pcBlock->message.sender;
      ActOnInit ();
      break;
    case 1: // Config
      ActOnConfig ();
      break;
    case 2: // Save config
      nIdentifier = ((int *)(pcBlock->message.data.reserved))[0];
      szFilename = pcBlock->message.data.reserved + 8;
      psConfigFile = memopen (szFilename, "a");
      sprintf (szSection, "Component %d", nIdentifier);
      SaveSectionMem (psConfigFile, szSection);
      ActOnConfigSave (psConfigFile);
      memclose (psConfigFile);
      err (xosfile_set_type (szFilename, COMPOSE_FILETYPE));
      break;
    case 3: // Load config
      nIdentifier = ((int *)(pcBlock->message.data.reserved))[0];
      szFilename = pcBlock->message.data.reserved + 8;
      psConfigFile = memopen (szFilename, "r");
      sprintf (szSection, "Component %d", nIdentifier);
      nSection = FindSectionMem (psConfigFile, szSection);
      ActOnConfigLoad (psConfigFile, nSection);
      memclose (psConfigFile);
      break;
  }

  REPORTVAR ("RECV- LinkControl handle = %d", gnLinkHandle);
  REPORTVAR ("      LinkControl action = %d", nAction);
}

//////////////////////////////////////////////////////////////////
// Receive message LINK_OPEN
void ReceiveLinkOpen (wimp_block *pcBlock)
{
  int                         nLinkHandle;
  int                         nLink;

  nLinkHandle = ((int *)(pcBlock->message.data.reserved))[0];
  if (nLinkHandle == gnLinkHandle)
  {
    nLink = ((int *)(pcBlock->message.data.reserved))[1];

    ActOnLinkOpen (nLink);

    REPORTVAR ("RECV: LinkOpen handle = %d", nLinkHandle);
    REPORTVAR ("      LinkOpen link = %d", nLink);
  }
}

//////////////////////////////////////////////////////////////////
// Receive message LINK_SEND
void ReceiveLinkSend (wimp_block *pcBlock)
{
  int                         nLinkHandle;
  int                         nLink;
  int                         nSize;
  char                        *pcData;

  nLinkHandle = ((int *)(pcBlock->message.data.reserved))[0];
  if (nLinkHandle == gnLinkHandle)
  {
    nLink = ((int *)(pcBlock->message.data.reserved))[1];
    nSize = ((int *)(pcBlock->message.data.reserved))[2];
    pcData =  pcBlock->message.data.reserved + 12;

    ActOnLinkSend (nLink, pcData, nSize);

    REPORTVAR ("RECV: LinkSend handle = %d", gnLinkHandle);
    REPORTVAR ("      LinkSend link = %d", nLink);
    REPORTVAR ("      LinkSend size = %d", nSize);
  }
}

//////////////////////////////////////////////////////////////////
// Receive message LINK_CLOSE
void ReceiveLinkClose (wimp_block *pcBlock)
{
  int                         nLinkHandle;
  int                         nLink;

  nLinkHandle = ((int *)(pcBlock->message.data.reserved))[0];
  if (nLinkHandle == gnLinkHandle)
  {
    nLink = ((int *)(pcBlock->message.data.reserved))[1];

    ActOnLinkClose (nLink);

    REPORTVAR ("RECV: LinkClose handle = %d", gnLinkHandle);
    REPORTVAR ("      LinkClose link = %d", nLink);
  }
}

//////////////////////////////////////////////////////////////////
// Receive message LINK_DATASAVE
void ReceiveLinkDataSave (wimp_block *pcBlock)
{
  int                         nLinkHandle;
  int                         nLink;

  nLinkHandle = ((int *)(pcBlock->message.data.reserved))[0];
  if (nLinkHandle == gnLinkHandle)
  {
    nLink = ((int *)(pcBlock->message.data.reserved))[1];

    REPORTVAR ("RECV: LinkDataSave handle = %d", gnLinkHandle);
    REPORTVAR ("      LinkDataSave link = %d", nLink);

    // Reply with a RamFetch message
    ((int *)(pcBlock->message.data.reserved))[2] = (int)gpcLinkBuffer;
    ((int *)(pcBlock->message.data.reserved))[3] = LINK_RAM_BUFFER;

    pcBlock->message.size = 36;
    pcBlock->message.your_ref = pcBlock->message.my_ref;
    pcBlock->message.action = message_LINK_RAMFETCH;

    xwimp_send_message (wimp_USER_MESSAGE, & pcBlock->message,
      pcBlock->message.sender);

    REPORTVAR ("SEND: LinkRamFetch handle = %d", gnLinkHandle);
    REPORTVAR ("      LinkRamFetch link = %d", nLink);
    REPORTVAR ("      LinkRamFetch size = %d", LINK_RAM_BUFFER);
  }
}

//////////////////////////////////////////////////////////////////
// Receive message LINK_RAMFETCH
void ReceiveLinkRamFetch (wimp_block *pcBlock)
{
  int                         nLinkHandle;
  int                         nLink;
  int                         nSize;
  char                        *pcBuffer;
  int                         nSendLen;

  nLinkHandle = ((int *)(pcBlock->message.data.reserved))[0];
  if (nLinkHandle == gnLinkHandle)
  {
    nLink = ((int *)(pcBlock->message.data.reserved))[1];
    pcBuffer = (char *)(((int *)(pcBlock->message.data.reserved))[2]);
    nSize = ((int *)(pcBlock->message.data.reserved))[3];

    REPORTVAR ("RECV: LinkRamFetch handle = %d", gnLinkHandle);
    REPORTVAR ("      LinkRamFetch link = %d", nLink);
    REPORTVAR ("      LinkRamFetch size = %d", nSize);

    // Transfer data
    if (gnLinkTransLeft < nSize)
    {
      nSendLen = gnLinkTransLeft;
    }
    else
    {
      nSendLen = nSize;
    }

    if (nSendLen > 0)
    {
      xwimp_transfer_block (gnTaskHandle, gpcLinkTransPos,
        pcBlock->message.sender, pcBuffer, nSendLen);
    }

    // Reply with a RamTransmit message
    ((int *)(pcBlock->message.data.reserved))[2] = (int)pcBuffer;
    ((int *)(pcBlock->message.data.reserved))[3] = nSendLen;

    pcBlock->message.size = 36;
    pcBlock->message.your_ref = pcBlock->message.my_ref;
    pcBlock->message.action = message_LINK_RAMTRANSMIT;

    xwimp_send_message (wimp_USER_MESSAGE, & pcBlock->message,
      pcBlock->message.sender);

    REPORTVAR ("SEND: LinkRamTransmit handle = %d", gnLinkHandle);
    REPORTVAR ("      LinkRamTransmit link = %d", nLink);
    REPORTVAR ("      LinkRamTransmit size = %d", nSendLen);

    gpcLinkTransPos += nSendLen;
    gnLinkTransLeft -= nSendLen;

    if (nSendLen < nSize)
    {
      // Everything has been transmitted
      gnLinkTransRef = -1;

      ActOnLinkSent (nLink);
    }
    else
    {
      gnLinkTransRef = pcBlock->message.my_ref;
    }
  }
}

//////////////////////////////////////////////////////////////////
// Receive message LINK_RAMTRANSMIT
void ReceiveLinkRamTransmit (wimp_block *pcBlock)
{
  int                         nLinkHandle;
  int                         nLink;
  int                         nSize;
  char                        *pcBuffer;

  nLinkHandle = ((int *)(pcBlock->message.data.reserved))[0];
  if (nLinkHandle == gnLinkHandle)
  {
    nLink = ((int *)(pcBlock->message.data.reserved))[1];
    nSize = ((int *)(pcBlock->message.data.reserved))[3];
    pcBuffer = (char *)(((int *)(pcBlock->message.data.reserved))[2]);

    REPORTVAR ("RECV: LinkRamTransmit handle = %d", gnLinkHandle);
    REPORTVAR ("      LinkRamTransmit link = %d", nLink);
    REPORTVAR ("      LinkRamTransmit size = %d", nSize);

    if (nSize > 0)
    {
      ActOnLinkSend (nLink, pcBuffer, nSize);
    }

    if (nSize == LINK_RAM_BUFFER)
    {
      // The buffer was full, so we should ask for some more

      // Reply with a RamFetch message
      ((int *)(pcBlock->message.data.reserved))[2] = (int)gpcLinkBuffer;
      ((int *)(pcBlock->message.data.reserved))[3] = LINK_RAM_BUFFER;
      ((int *)(pcBlock->message.data.reserved))[4] = 0;

      pcBlock->message.size = 40;
      pcBlock->message.your_ref = pcBlock->message.my_ref;
      pcBlock->message.action = message_LINK_RAMFETCH;

      xwimp_send_message (wimp_USER_MESSAGE, & pcBlock->message,
        pcBlock->message.sender);

      REPORTVAR ("SEND: LinkRamFetch handle = %d", gnLinkHandle);
      REPORTVAR ("      LinkRamFetch link = %d", nLink);
      REPORTVAR ("      LinkRamFetch size = %d", LINK_RAM_BUFFER);
    }
  }
}

//////////////////////////////////////////////////////////////////
// Send message LINK_OPEN
void LinkOpen (int nLink)
{
  wimp_message                sMessage;

  if (ghLinkTask != NULL)
  {
    ((int *)(sMessage.data.reserved))[0] = gnLinkHandle;
    ((int *)(sMessage.data.reserved))[1] = nLink;

    sMessage.size = 28;
    sMessage.your_ref = 0;
    sMessage.action = message_LINK_OPEN;

    xwimp_send_message (wimp_USER_MESSAGE, & sMessage, ghLinkTask);

    REPORTVAR ("SEND: LinkOpen handle = %d", gnLinkHandle);
    REPORTVAR ("      LinkOpen link = %d", nLink);
  }
}

//////////////////////////////////////////////////////////////////
// Send message LINK_SEND
void LinkSend (int nLink, char * pcData, int nSize)
{
  wimp_message                sMessage;

  if (ghLinkTask != NULL)
  {
    if (nSize <= LINK_SEND_MAX)
    {
      // Can be sent in a single message
      ((int *)(sMessage.data.reserved))[0] = gnLinkHandle;
      ((int *)(sMessage.data.reserved))[1] = nLink;
      ((int *)(sMessage.data.reserved))[2] = nSize;
      memcpy (sMessage.data.reserved + 12, pcData, nSize);

      sMessage.size = WORDALIGN ((32 + nSize));
      sMessage.your_ref = 0;
      sMessage.action = message_LINK_SEND;

      xwimp_send_message (wimp_USER_MESSAGE, & sMessage, ghLinkTask);

      ActOnLinkSent (nLink);

      REPORTVAR ("SEND: LinkSend handle = %d", gnLinkHandle);
      REPORTVAR ("      LinkSend link = %d", nLink);
      REPORTVAR ("      LinkSend size = %d", nSize);
    }
    else
    {
      // Needs to be sent using ram transfers
      ((int *)(sMessage.data.reserved))[0] = gnLinkHandle;
      ((int *)(sMessage.data.reserved))[1] = nLink;
      ((int *)(sMessage.data.reserved))[2] = nSize;

      sMessage.size = 32;
      sMessage.your_ref = 0;
      sMessage.action = message_LINK_DATASAVE;

      xwimp_send_message (wimp_USER_MESSAGE, & sMessage, ghLinkTask);

      REPORTVAR ("SEND: LinkDataSave handle = %d", gnLinkHandle);
      REPORTVAR ("      LinkDataSave link = %d", nLink);
      REPORTVAR ("      LinkDataSave size = %d", nSize);

      gpcLinkTransPos = pcData;
      gnLinkTransLeft = nSize;
    }
  }
}

//////////////////////////////////////////////////////////////////
// Send message LINK_CLOSE
void LinkClose (int nLink)
{
  wimp_message                sMessage;

  if (ghLinkTask != NULL)
  {
    ((int *)(sMessage.data.reserved))[0] = gnLinkHandle;
    ((int *)(sMessage.data.reserved))[1] = nLink;

    sMessage.size = 28;
    sMessage.your_ref = 0;
    sMessage.action = message_LINK_CLOSE;

    xwimp_send_message (wimp_USER_MESSAGE, & sMessage, ghLinkTask);

    REPORTVAR ("SEND: LinkClose handle = %d", gnLinkHandle);
    REPORTVAR ("      LinkClose link = %d", nLink);
  }
}

//////////////////////////////////////////////////////////////////
// Set up the configuration window
void ConfigSetUpWindow (void)
{
  char                        szText[1024];

  sprintf (szText, "%d", gnSendSize);
  SetIconText (szText, gwhConf, 3);
}

//////////////////////////////////////////////////////////////////
// Set configuration from the configuration window
void ConfigSetFromWindow (void)
{
  sscanf (GetIconText (gwhConf, 3), "%d", & gnSendSize);
}

//////////////////////////////////////////////////////////////////
// Act on initialisation
void ActOnInit (void)
{
}

//////////////////////////////////////////////////////////////////
// Act on config request
void ActOnConfig (void)
{
  ConfigSetUpWindow ();
  OpenWindowInitCentre (gwhConf);
  xwimp_set_caret_position (gwhConf, (wimp_i)3, 0, 0, -1, -1);
}

//////////////////////////////////////////////////////////////////
// Act on config save request
void ActOnConfigSave (MemFile * psMemFile)
{
  char                        szValue[1024];

  sprintf (szValue, "%d", gnSendSize);
  SaveDetailMem (psMemFile, "Size", szValue);
}

//////////////////////////////////////////////////////////////////
// Act on config load request
void ActOnConfigLoad (MemFile * psMemFile, int nSection)
{
  char                        szValue[1024];

  FindValueMem (psMemFile, nSection, "Size", szValue, sizeof (szValue));
  sscanf (szValue, "%d", & gnSendSize);
}

//////////////////////////////////////////////////////////////////
// Act on the opening of a link
void ActOnLinkOpen (int nLink)
{
  int                         nSuccess;

  if (nLink == 0)
  {
    nSuccess = flex_alloc ((flex_ptr)(& gpcDataIn), DATAIN_CHUNK_SIZE);
    if (nSuccess == 1)
    {
      gnDataInPos = 0;
      gnDataInSize = DATAIN_CHUNK_SIZE;
    }
    else
    {
      gnDataInPos = -1;
      ShowWarningTag ("Er5");
    }
  }
  else
  {
    gnDataInPos = -1;
    ShowWarningTag ("Er6");
  }
}

//////////////////////////////////////////////////////////////////
// Act on the closing of a link
void ActOnLinkClose (int nLink)
{
  int                         nSuccess;

  if (nLink == 0)
  {
    if (gnDataOutNum < FILE_LOAD_MAX)
    {
      gnDataInSize = gnDataInPos;
      nSuccess = Evaluate (& gapcDataOut[gnDataOutNum],
        & ganDataOutSize[gnDataOutNum], & gpcDataIn, & gnDataInSize);
      if (nSuccess == 1)
      {
        gnDataOutNum++;
      }
    }
    else
    {
      ShowWarningTag ("Er8");
      flex_free ((flex_ptr)(& gpcDataIn));
      gnDataInSize = 0;
    }
  }
}

//////////////////////////////////////////////////////////////////
// Act on the receiving of data on a link
void ActOnLinkSend (int nLink, char * pcData, int nSize)
{
  int                         nSuccess;
  int                         nNewSize;

  if (nLink == 0)
  {
    if (gnDataInPos >= 0)
    {
      // Extend the memory slot if necessary
      if ((gnDataInPos + nSize) >= gnDataInSize)
      {
        // Allocate some more memory
        nNewSize = (((gnDataInPos + nSize) / DATAIN_CHUNK_SIZE) + 1)
          * DATAIN_CHUNK_SIZE;

        nSuccess = flex_extend ((flex_ptr)(& gpcDataIn), nNewSize);
        if (nSuccess == 1)
        {
          gnDataInSize = nNewSize;
        }
        else
        {
          gnDataInPos = -1;
          ShowWarningTag ("Er7");
        }
      }

      // Transfer the data into a memory slot
      if ((gnDataInPos + nSize) < gnDataInSize)
      {
        memcpy ((gpcDataIn + gnDataInPos), pcData, nSize);
        gnDataInPos += nSize;
      }
    }
  }
}

//////////////////////////////////////////////////////////////////
// Act on the data having been sent
void ActOnLinkSent (int nLink)
{
  nLink = nLink;
}

//////////////////////////////////////////////////////////////////
// Poll 0: Null Poll
void NullPoll (wimp_block *pcBlock)
{
  int                         nSendAmount;
  int                         nCount;

  pcBlock = pcBlock;

  // If there are any files in our queue, we need to send them
  if (gnDataOutNum > 0)
  {
    if (gnDataSend < 0)
    {
      // Open up a new channel to send data on
      LinkOpen (0);
      gnDataSend = 0;
    }
    else
    {
      // Send some more data
      nSendAmount = ganDataOutSize[0] - gnDataSend;
      if (nSendAmount > gnSendSize)
      {
        nSendAmount = gnSendSize;
      }

      if (nSendAmount > 0)
      {
        // Send the data
        LinkSend (0, gapcDataOut[0] + gnDataSend, nSendAmount);
        gnDataSend += nSendAmount;
      }
      else
      {
        // We've sent everything, so we're done with this file
        flex_free ((flex_ptr)(& gapcDataOut[0]));
        gapcDataOut[0] = NULL;
        ganDataOutSize[0] = 0;
        gnDataSend = -1;
        gnDataOutNum--;

        for (nCount = 0; nCount < gnDataOutNum; nCount++)
        {
          flex_reanchor ((flex_ptr)(& gapcDataOut[nCount]),
            (flex_ptr)(& gapcDataOut[nCount + 1]));
          ganDataOutSize[nCount] = ganDataOutSize[nCount + 1];
        }

        LinkClose(0);
      }
    }
  }
}

//////////////////////////////////////////////////////////////////
// Evaluate the data to convert it from an input to an output
int Evaluate (char * * ppcDataOut, int * pnDataOutSize, char * * ppcDataIn, int * pnDataInSize)
{
  int                         nSuccess;

  // Allocate memory for data out
  nSuccess = flex_alloc ((flex_ptr)(ppcDataOut), * pnDataInSize);
  if (nSuccess == 1)
  {
    *pnDataOutSize = *pnDataInSize;
    memcpy (*ppcDataOut, *ppcDataIn, *pnDataOutSize);
  }
  else
  {
    ShowWarningTag ("Er9");
  }

  // Free up the input memory
  flex_free ((flex_ptr)(ppcDataIn));
  *pnDataInSize = NULL;

  return nSuccess;
}
