//////////////////////////////////////////////////////////////////
// Igor
//
// Simple Telnet Server
// Flying Pig
// Started 05/8/2004
//////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////
// 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 "oslib/socket.h"

#include "Igor.h"
#include "md5.h"
#include "MemFile.h"

#include "flexlib/flex.h"

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

#include "netinet/in.h"
#include "sys/select.h"
#include "arpa/inet.h"
#include "Unix:sys/socket.h"

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

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

#define WORDALIGN(b)          (((b) + 3) & ~3)
#define TEXT_BUFFER_SIZE      (2*1024)
#define LINE_CHARS_MAX        (64)
#define LINE_LINES_MAX        (64)
#define TASK_TEXT_HANDLE      (0x100u)
#define LISTEN_BACKLOG        (10)
#define RECEIVE_BUFFER_SIZE   (1024)
#define SCREEN_X_BORDER       (16)
#define SCREEN_Y_BORDER       (16)
#define SCREEN_Y_LINE         (32)

#define USER_SAVE             "<Igor$ChoicesWrite>.Choices"
#define USER_SAVE_DIR         "<Igor$ChoicesWrite>"
#define USER_LOAD             "<Igor$ChoicesDir>.Choices"
#define PASSWORD_MAX          (256)
#define PORT_STRING_MAX       (6)
#define STRING_LEN            (1024)

#define POLLTIME_NONE         (-1)
#define POLLTIME_FAST         (0)
#define POLLTIME_LISTEN       (30)
#define POLLTIME_ACTIVE       (1)

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

typedef enum
{
  SOCK_INVALID = -1,

  SOCK_OK,
  SOCK_ERR,
  SOCK_FAIL,
  SOCK_WAIT,
  SOCK_RECEIVE,
  SOCK_MULTIDONE,

  SOCK_NUM
} SOCK;

typedef enum
{
  STATE_INVALID = -1,

  STATE_CLOSED,
  STATE_CREATELISTENING,
  STATE_CREATEDLISTENING,
  STATE_LISTENING,
  STATE_LOGIN,
  STATE_PASSWORD,
  STATE_ACCEPTED,
  STATE_CLOSEACCEPTED,
  STATE_CLOSELISTENING,

  STATE_NUM
} STATE;

//////////////////////////////////////////////////////////////////
// Function prototypes

void RedrawWindow (wimp_block *pcBlock);
void RedrawMain (wimp_draw *psRedraw);
void AddTextBlock (char * szText, int nSize);
void SendToChild (char * szText, int nSize);
void TaskWindowOutput (wimp_block *pcBlock);
void TaskWindowEgo (wimp_block *pcBlock);
void TaskWindowMorio (wimp_block *pcBlock);
void StartChildTask (void);
void KillChild (void);
void NullPoll (void);
void StartListening (void);
SOCK CreateSocket (socket_s * pnSocket);
SOCK ListenSocket (socket_s * pnSocket);
SOCK CloseSocket (socket_s * pnSocket);
SOCK Listen (socket_s * pnListen, socket_sockaddr * psAddress, socket_s * pnAccept);
SOCK ReadSocket (socket_s * pnSocket, socket_s * pnListen);
SOCK SocketSend (socket_s * pnSocket, char * szText, int nSize);
int CleanText (char * szFrom, int nFromSize, char * szTo, int nToSize);
int CleanReceived (char * szFrom, int nFromSize, char * szTo, int nToSize);
SOCK StateLogin (socket_s * pnSocket, socket_s * pnListen);
SOCK StatePassword (socket_s * pnSocket, socket_s * pnListen);
void SetIconSprite (char * szSprite, wimp_w whWindow, wimp_i ihIcon);
void EncodeMD5 (char * pcIn, int nInSize, char * pcOut);
void EncodeHMACMD5 (char * pcIn, int nInSize, char * pcKey, int nKeySize, char * pcOut);
void SetChoicesDetails (void);
void SetChoicesDialogue (void);
void OpenChoicesDialogue (void);
bool CheckUserDetails (char * szUserName, char * szPassword);
void CloseAllConnections (void);
void DeleteIconbarIcon (wimp_i ihIcon);
void UpdateIconbarIcon (void);
void SaveChoicesDetails (void);
bool LoadChoicesDetails (void);
void TaskInitialise (wimp_block *pcBlock);
void SetUpSaveFile (void);
void SaveMenuSelect (void);
void ClearConsole (void);
void SetState (STATE eState);

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

static char                   gpcTemp[256];
static char                   *gpcMessages;
static wimp_w                 gwhMain;
static wimp_w                 gwhSave;
static wimp_w                 gwhWarn;
static wimp_w                 gwhCont;
static wimp_w                 gwhConn;
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              *gpcMainWinMenu;
static wimp_t                 gnTaskHandle;
static bool                   gboDrag = FALSE;
static char                   gacFontRef[255];
static int                    gnMessageMyRef = 0;
static SAVETYPE               geSaveType = SAVETYPE_INVALID;
static int                    gnSaveFileType;
static char                   gszSaveString[255];
static char                   gszTextBuffer[TEXT_BUFFER_SIZE];
//static char                   gszCleanBuffer[TEXT_BUFFER_SIZE * 2];
static int                    gnBuffStart;
static wimp_t                 gnChildHandle;
static bool                   gboChildStarted;
static STATE                  geState;
static socket_s               gnListenSocket;
static socket_s               gnAcceptSocket;
static char                   gpcBuffer[RECEIVE_BUFFER_SIZE];
static char                   gpcCleanReceived[RECEIVE_BUFFER_SIZE];
static int                    gnLocalEcho;
static char                   gpcSendBuffer[234];
static char                   gszUsernameTemp[1024];
static char                   gszPasswordTemp[1024];
static char                   gszPrintTemp[1024];
static int                    gnUserPassPos;
static osspriteop_area        *gpcSprites;
static char                   gszScreenBuf[LINE_LINES_MAX][LINE_CHARS_MAX + 1];
static int                    gnScreenBufStart;
static int                    gnScreenBufEnd;
static int                    gnScreenBufChar;
static char                   gszUserName[256];
static char                   gpcPassword[16];
static bool                   gboCloseAllConnections;
static bool                   gboAcceptIncoming;
static bool                   gboNotify;
static bool                   gboHideIcon;
static int                    gnPort;
static bool                   gboIconCreated;
static char                   gszProgTitle[128];
static int                    gnRamTransRef;
static char *                 gpcRamTransPos;
static int                    gnRamTransLeft;
static char                   gpcSaveBuffer[LINE_LINES_MAX * (LINE_CHARS_MAX + 1)];
static int                    gnSaveBufferSize;
static int                    gnTimeOut;
static int                    gnActionTime;
static STATE                  gePreviousState = STATE_INVALID;
static int                    gnPollTime;

//////////////////////////////////////////////////////////////////
// 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;
  unsigned int                uFlags;
  int                         nNextPoll;
  wimp_MESSAGE_LIST(13) sMessages = {{message_DATA_SAVE,
                                      message_DATA_SAVE_ACK,
                                      message_DATA_LOAD,
                                      message_DATA_OPEN,
                                      message_RAM_FETCH,
                                      message_RAM_TRANSMIT,
                                      message_TASK_INITIALISE,
                                      0x502u, 0x4af80u,
                                      0x808c1, // TaskWindow_Output
                                      0x808c2, // TaskWindow_Ego
                                      0x808c3, // TaskWindow_Morio
                                      0u}};
  wimp_message_list *psUserMessages = (wimp_message_list*)&sMessages;

  // 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 sprites
  gpcSprites = LoadSprites (SPRITES);

  // Load templates
  for (nCount = 0; nCount < 256; nCount++)
  {
    gacFontRef[nCount] = 0;
  }
  wimp_open_template (TEMPLATES);
  gwhMain = LoadTemplate ("Main");
  gwhWarn = LoadTemplate ("Warning");
  gwhCont = LoadTemplate ("Control");
  gwhConn = LoadTemplate ("Connect");

  // Create menus
  gpcIconBarMenu = CreateMenu (Tag ("Menu1"));
  gpcMainWinMenu = CreateMenu (Tag ("Menu2"));
  gwhSave = gawhMenuWinHandle[1];

  // Close templates
  wimp_close_template ();

  // Initialise variables
  gnBuffStart = 0;
  memset (gszTextBuffer, 0x20, sizeof(gszTextBuffer));
  gszTextBuffer[sizeof(gszTextBuffer) - 1] = 0x0d;
  gszTextBuffer[0] = 0;
  gnChildHandle = 0;
  gboChildStarted = FALSE;
  geState = STATE_CLOSED;
  gnListenSocket = SOCKET_NOT_SET;
  gnAcceptSocket = SOCKET_NOT_SET;
  gnLocalEcho = 0;
  gboCloseAllConnections = TRUE;
  gnActionTime = 0;
  gnPollTime = 0;

  for (nCount = 0; nCount < LINE_LINES_MAX; nCount++)
  {
    gszScreenBuf[nCount][0] = 0;
  }
  gnScreenBufStart = 0;
  gnScreenBufEnd = 0;
  gnScreenBufChar = 0;

  gszUserName[0] = 0;
  memset (gpcPassword, 0, sizeof (gpcPassword));
  gboAcceptIncoming = FALSE;
  gboNotify = FALSE;
  gboHideIcon = FALSE;
  gnPort = 23;
  gnTimeOut = 30000;

  // Load choices
  LoadChoicesDetails ();
  if (gboAcceptIncoming)
  {
    gboCloseAllConnections = FALSE;
    if (geState == STATE_CLOSED)
    {
      SetState (STATE_CREATELISTENING);
    }
  }
  else
  {
    CloseAllConnections ();
  }

  // Create icon
  gboIconCreated = FALSE;
  UpdateIconbarIcon ();

  // Main polling loop
  //////////////////////////////////////////////////////////////////
  do
  {
    if (gnPollTime == POLLTIME_NONE)
    {
      uFlags = FLAGS_NONULL;
    }
    else
    {
      uFlags = FLAGS_NULL;
    }

    xos_read_monotonic_time (& nNextPoll);
    nNextPoll += gnPollTime;
    nEvent = wimp_poll_idle (uFlags, & cBlock, nNextPoll, NULL);
    switch (nEvent)
    {
      case  0: // Null
        NullPoll ();
        break;
      case  1: // Redraw window
        RedrawWindow (& cBlock);
        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_TASK_INITIALISE:
      TaskInitialise (pcBlock);
      break;
    case message_HELP:
      Help (pcBlock);
      break;
    case 0x808c1: // TaskWindow_Output
      TaskWindowOutput (pcBlock);
      break;
    case 0x808c2: // TaskWindow_Ego
      TaskWindowEgo (pcBlock);
      break;
    case 0x808c3: // TaskWindow_Morio
      TaskWindowMorio (pcBlock);
      break;
  }
}

//////////////////////////////////////////////////////////////////
// Poll 19: Message acknowledge received
void Acknowledged (wimp_block *pcBlock)
{
  switch (pcBlock->message.action)
  {
    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 sockets
  if (gnListenSocket != SOCKET_NOT_SET)
  {
    CloseSocket (& gnListenSocket);
  }
  if (gnAcceptSocket != SOCKET_NOT_SET)
  {
    CloseSocket (& gnAcceptSocket);
  }

  // Kill the child task
  KillChild ();

  // 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 - 64, 96 + 44 * 3);
        break;
      case wimp_CLICK_ADJUST:
        StartListening ();
        break;
    }
  }

  if (whWindow == gwhMain)
  {
    if (nButton == wimp_CLICK_MENU)
    {
      OpenMenu (gpcMainWinMenu, 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 == gwhCont)
  {
    switch (ihIcon)
    {
      case (wimp_i)12: // Cancel
        SetChoicesDialogue ();
        if (nButton != wimp_CLICK_ADJUST)
        {
          CloseWindowHandle (gwhCont);
        }
        break;
      case (wimp_i)13: // OK
        SetChoicesDetails ();
        if (nButton != wimp_CLICK_ADJUST)
        {
          CloseWindowHandle (gwhCont);
        }
        break;
      case (wimp_i)16: // Save
        SetChoicesDetails ();
        SaveChoicesDetails ();
        if (nButton != wimp_CLICK_ADJUST)
        {
          CloseWindowHandle (gwhCont);
        }
        break;
      case (wimp_i)11: // Close all connections
        CloseAllConnections ();
        SetIconSelectionState (FALSE, gwhCont, 5);
        break;
    }
  }

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

  if (whWindow == gwhConn)
  {
    switch (ihIcon)
    {
      case (wimp_i)1: // OK
        CloseWindowHandle (gwhConn);
        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 == gwhCont)
      {
        SetChoicesDetails ();
        CloseWindowHandle (gwhCont);
      }
      break;
    case wimp_KEY_ESCAPE:
      if (whWindow == gwhWarn)
      {
        CloseWarning ();
      }
      if (whWindow == gwhCont)
      {
        CloseWindowHandle (whWindow);
      }
      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:
        OpenChoicesDialogue ();
        break;
      case 2:
        Quit ();
        break;
      default:
        break;
    }
  }

  if (gpcMenuCurrent == gpcMainWinMenu)
  {
    switch (pcBlock->selection.items[0])
    {
      case 0:
        SaveMenuSelect ();
        break;
      case 1:
        OpenChoicesDialogue ();
        break;
      case 2:
        ClearConsole ();
        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:
        SetUpSaveFile ();

        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 = gnSaveBufferSize;
        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 with a sprite area
wimp_i CreateIconbarIconSprites (char * szSprite, int nWidth, int nHeight, osspriteop_area * pcArea)
{
  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_ICON_INDIRECTED |
    (wimp_BUTTON_CLICK << wimp_ICON_BUTTON_TYPE_SHIFT);
  sIconBlock.icon.data.indirected_sprite.id = calloc (32, 1);
  strncpy ((char*)sIconBlock.icon.data.indirected_sprite.id, szSprite, 32);
  sIconBlock.icon.data.indirected_sprite.area = pcArea;
  sIconBlock.icon.data.indirected_sprite.size = 32;

  ihIcon = wimp_create_icon (& sIconBlock);

  return ihIcon;
}

//////////////////////////////////////////////////////////////////
// Delete an iconbar icon
void DeleteIconbarIcon (wimp_i ihIcon)
{
  xwimp_delete_icon (wimp_ICON_BAR_RIGHT, ihIcon);
}

//////////////////////////////////////////////////////////////////
// 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;
}

//////////////////////////////////////////////////////////////////
// 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)
{
// Do nothing
  pcBlock = pcBlock;

//  // Check filetype
//  if (pcBlock->message.data.data_xfer.file_type == 0xfff)
//  {
//    pcBlock->message.size = 28;
//    pcBlock->message.your_ref = pcBlock->message.my_ref;
//    pcBlock->message.action = 6;
//    pcBlock->message.data.ram_xfer.addr = malloc (1024 * 10);
//    pcBlock->message.data.ram_xfer.size = (1024 * 10);
//
//    xwimp_send_message (wimp_USER_MESSAGE, & pcBlock->message,
//      pcBlock->message.sender);
///*
//    pcBlock->message.size = 60;
//    pcBlock->message.your_ref = pcBlock->message.my_ref;
//    pcBlock->message.action = 2;
//    strcpy (pcBlock->message.data.data_xfer.file_name, "<Wimp$Scrap>");
//
//    xwimp_send_message (wimp_USER_MESSAGE, pcBlock->message,
//      pcBlock->message.sender);
//*/
//  }

// Do nothing
}

//////////////////////////////////////////////////////////////////
// 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 = 3;

        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 = 3;

        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)
{
// Do nothing
  pcBlock = 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
//    pcLoadedFile = LoadFile (szFilename, & nSize);
//
//    if (pcLoadedFile)
//    {
//      pcBlock->message.your_ref = pcBlock->message.my_ref;
//      pcBlock->message.action = 4;
//
//      xwimp_send_message (wimp_USER_MESSAGE, & pcBlock->message,
//        pcBlock->message.sender);
//
//      FileLoaded (pcLoadedFile, nSize);
//    }
//  }

// Do nothing
}

//////////////////////////////////////////////////////////////////
// User has double clicked on a file
void DataOpen (wimp_block *pcBlock)
{
// Do nothing
  pcBlock = 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
//    pcLoadedFile = LoadFile (szFilename, & nSize);
//
//    if (pcLoadedFile)
//    {
//      pcBlock->message.your_ref = pcBlock->message.my_ref;
//      pcBlock->message.action = 4;
//
//      xwimp_send_message (wimp_USER_MESSAGE, & pcBlock->message,
//        pcBlock->message.sender);
//
//      FileLoaded (pcLoadedFile, nSize);
//    }
//  }

// Do nothing
}

//////////////////////////////////////////////////////////////////
// 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)
  {
    CloseMenu ();

    switch (geSaveType)
    {
      case SAVETYPE_FILE:
        SetUpSaveFile ();
        gpcRamTransPos = gpcSaveBuffer;
        gnRamTransLeft = gnSaveBufferSize;
        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
    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)
{
// Do nothing
  pcBlock = pcBlock;

//  char *                      pcLoadedFile;
//  int                         nSize;

//  pcLoadedFile = pcBlock->message.data.ram_xfer.addr;
//  nSize = pcBlock->message.data.ram_xfer.size;

  // Save the file out, for the sake of the example
//  FileLoaded (pcLoadedFile, nSize);

// Do nothing
}

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

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

   // Icon bar
  if (whWindow == wimp_ICON_BAR)
  {
    nWinNum = 1;
  }

  // Main window
  if (whWindow == gwhMain)
  {
    nWinNum = 2;
  }

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

  // Information window
  if (whWindow == gawhMenuWinHandle[0])
  {
    nWinNum = 4;
  }

  // Save window
  if (whWindow == gwhSave)
  {
    nWinNum = 5;
  }

  // Control window
  if (whWindow == gwhCont)
  {
    nWinNum = 6;
  }

  // Connect window
  if (whWindow == gwhConn)
  {
    nWinNum = 7;
  }

  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);

    if (sSelection.items[1] == -1)
    {
      if (gpcMenuCurrent == gpcIconBarMenu)
      {
        nWinNum = 8;
      }
      if (gpcMenuCurrent == gpcMainWinMenu)
      {
        nWinNum = 9;
      }
    }
    if (nWinNum)
    {
      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 with an argument
char * TagArg (char * szTag, char * szArg0)
{
  messagetrans_lookup ((messagetrans_control_block*)gpcMessages,
    szTag, gpcTemp, 256, szArg0, 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;

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

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

    if (pcMemory)
    {
      err (xosfile_load_stamped_no_path (szFilename, pcMemory,  NULL,
        NULL, NULL, NULL, NULL));
    }
  }
  else
  {
    pcMemory = NULL;
  }

  return pcMemory;
}

//////////////////////////////////////////////////////////////////
// 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)
  {
    nSize += 4;
    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

//////////////////////////////////////////////////////////////////
// File loaded routine
void FileLoaded (char * pcMemory, int nSize)
{
  // Save the file out, for the sake of the example
//  err (xosfile_save_stamped (GetIconText (gwhSave, 2), 0xffd, pcMemory,
//    (pcMemory + nSize)));

  AddTextBlock (pcMemory, nSize);

  // Free up the memory
  free (pcMemory);
}

//////////////////////////////////////////////////////////////////
// Save file routine - saves the example file out
void SaveFile (char * szFilename)
{
  SetUpSaveFile ();

  err (xosfile_save_stamped (szFilename, gnSaveFileType,  gpcSaveBuffer,
    gpcSaveBuffer + gnSaveBufferSize));
}

//////////////////////////////////////////////////////////////////
// Save file routine - saves the example file out
void SetUpSaveFile (void)
{
  int                         nLine;
  int                         nChar;
  int                         nPosTo;

  // Copy over a character at a time from each line
  nLine = gnScreenBufStart;
  nChar = 0;
  nPosTo = 0;

  while (nLine != gnScreenBufEnd)
  {
    while ((gszScreenBuf[nLine][nChar] > 0) && (nChar < LINE_CHARS_MAX))
    {
      gpcSaveBuffer[nPosTo] = gszScreenBuf[nLine][nChar];
      nPosTo++;
      nChar++;
    }
    // Next line
    gpcSaveBuffer[nPosTo] = 0x0a;
    nPosTo++;
    nChar = 0;
    nLine++;
    if (nLine >= LINE_LINES_MAX)
    {
      nLine = 0;
    }
  }

  // The last line
  while (nChar <= gnScreenBufChar)
  {
    gpcSaveBuffer[nPosTo] = gszScreenBuf[nLine][nChar];
    nPosTo++;
    nChar++;
  }

  gnSaveBufferSize = nPosTo - 1;
}

//////////////////////////////////////////////////////////////////
// Poll 1: Redraw window given window block
void    RedrawWindow          (wimp_block *pcBlock)
{
  bool                        boMore;

  xwimp_redraw_window (& pcBlock->redraw, & boMore);
  while (boMore)
  {
    RedrawMain (& pcBlock->redraw);
    xwimp_get_rectangle (& pcBlock->redraw, & boMore);
  }
}

//////////////////////////////////////////////////////////////////
// Redraw the main window
void RedrawMain (wimp_draw *psRedraw)
{
//  char                  szLine[LINE_CHARS_MAX];
  int                   nVisMinX = psRedraw->box.x0;
  //int                   nVisMinY = psRedraw->box.y0;
  //int                   nVisMaxX = psRedraw->box.x1;
  int                   nVisMaxY = psRedraw->box.y1;

  int                   nRedMinX = psRedraw->clip.x0;
  int                   nRedMinY = psRedraw->clip.y0;
  int                   nRedMaxX = psRedraw->clip.x1;
  int                   nRedMaxY = psRedraw->clip.y1;

  int                   nScrollX = psRedraw->xscroll;
  int                   nScrollY = psRedraw->yscroll;

//  int                   nLineStart;
//  int                   nLineEnd;
  int                   nPos;
  int                   nLine;
//  int                   nTempPos;

  // Fill in the background
  xwimp_set_colour (wimp_COLOUR_VERY_LIGHT_GREY);
  xos_plot (os_PLOT_POINT | os_MOVE_TO, nRedMinX, nRedMinY);
  xos_plot (os_PLOT_RECTANGLE | os_PLOT_TO, nRedMaxX, nRedMaxY);

  // Plot the text
  xos_join_cursors ();
  xwimp_set_colour (wimp_COLOUR_BLACK);

  nLine = 0;
  nPos = gnScreenBufStart;
  while (nPos != gnScreenBufEnd)
  {
    // Plot line
    xos_plot (os_PLOT_POINT | os_MOVE_TO,
      nVisMinX + SCREEN_X_BORDER - nScrollX,
      nVisMaxY - (SCREEN_Y_LINE * nLine) - nScrollY - SCREEN_Y_BORDER);
    printf (gszScreenBuf[nPos]);

    nPos++;
    nLine++;
    if (nPos >= LINE_LINES_MAX)
    {
      nPos = 0;
    }
  }

  // Plot the last line
  xwimp_set_colour (wimp_COLOUR_RED);
  xos_plot (os_PLOT_POINT | os_MOVE_TO,
    nVisMinX + SCREEN_X_BORDER - nScrollX,
    nVisMaxY - (SCREEN_Y_LINE * nLine) - nScrollY - SCREEN_Y_BORDER);
  printf (gszScreenBuf[nPos]);

//  nLine = 1;
//  nLineStart = gnBuffStart + 1;
//  if (nLineStart >= (int)sizeof(gszTextBuffer))
//  {
//    nLineStart = 0;
//  }
//  nLineEnd = nLineStart;
//  while (gszTextBuffer[nLineEnd] != 0)
//  {
//    // Copy a line over
//    nLineEnd = nLineStart;
//    nPos = 0;
//    while ((gszTextBuffer[nLineEnd] != 0)
//      && (gszTextBuffer[nLineEnd] != 0x0a)
//      && (gszTextBuffer[nLineEnd] != 0x0d)
//      && (nPos < (LINE_CHARS_MAX - 1)))
//    {
//      szLine[nPos] = gszTextBuffer[nLineEnd];
//      nPos++;
//      nLineEnd++;
//      if (nLineEnd >= (int)sizeof(gszTextBuffer))
//      {
//        nLineEnd = 0;
//      }
//    }
//    if ((gszTextBuffer[nLineEnd] == 0x0a)
//      || (gszTextBuffer[nLineEnd] == 0x0d))
//    {
//      nTempPos = nLineEnd + 1;
//      if (nTempPos >= (int)sizeof(gszTextBuffer))
//      {
//        nTempPos = 0;
//      }
//      if (((gszTextBuffer[nLineEnd] == 0x0a) &&
//        (gszTextBuffer[nTempPos] == 0x0d))
//        || (((gszTextBuffer[nLineEnd] == 0x0d) &&
//        (gszTextBuffer[nTempPos] == 0x0a))))
//      {
//        nLineEnd = nTempPos;
//      }
//      nLineStart = nLineEnd + 1;
//    }
//    else
//    {
//      nLineStart = nLineEnd;
//    }
//
//    szLine[nPos] = 0;
//    xos_plot (os_PLOT_POINT | os_MOVE_TO, nVisMinX + 16 - nScrollX,
//      nVisMaxY - (32 * nLine) - nScrollY);
//    printf (szLine);
//    nLine++;
//  }
}

//////////////////////////////////////////////////////////////////
// Add a block of text to the buffer and screen
void AddTextBlock (char * szText, int nSize)
{
//  int                   nCopyPos;
//  int                   nCopySize;
  wimp_draw             sRedraw;
  wimp_window_state     sState;
  bool                  boMore;
  int                   nCopyFrom;
//  int                   nCleanSize;


  // Transfer the text, a character at a time
  nCopyFrom = 0;
  while (nCopyFrom < nSize)
  {
    if (szText[nCopyFrom] < 0x20)
    {
      switch (szText[nCopyFrom])
      {
        case 0x0a:
          gszScreenBuf[gnScreenBufEnd][gnScreenBufChar] = 0;
          gnScreenBufChar = LINE_CHARS_MAX;
          if (((nCopyFrom + 1) < nSize) && (szText[nCopyFrom + 1] == 0x0d))
          {
            nCopyFrom++;
          }
          break;
        case 0x0d:
          gszScreenBuf[gnScreenBufEnd][gnScreenBufChar] = 0;
          gnScreenBufChar = LINE_CHARS_MAX;
          if (((nCopyFrom + 1) < nSize) && (szText[nCopyFrom + 1] == 0x0a))
          {
            nCopyFrom++;
          }
          break;
        case 0x08:
        case 0x09:
          gszScreenBuf[gnScreenBufEnd][gnScreenBufChar] = szText[nCopyFrom];
          gnScreenBufChar++;
          break;
        case 0x07:
        case 0x11:
          nCopyFrom += 1;
          break;
        case 0x12:
        case 0x16:
        case 0x1f:
          nCopyFrom += 2;
          break;
        case 0x17:
        case 0x19:
          nCopyFrom += 9;
          break;
        case 0x1c:
          nCopyFrom += 4;
          break;
        case 0x1d:
          nCopyFrom += 8;
          break;
        case 0x18:
          nCopyFrom += 16;
          break;
        default:
          // Do nothing
          break;
      }
    }
    else
    {
      gszScreenBuf[gnScreenBufEnd][gnScreenBufChar] = szText[nCopyFrom];
      gnScreenBufChar++;
    }
    nCopyFrom++;

    if (gnScreenBufChar >= LINE_CHARS_MAX)
    {
      // New line
      gszScreenBuf[gnScreenBufEnd][LINE_CHARS_MAX] = 0;
      gnScreenBufChar = 0;

      gnScreenBufEnd++;
      if (gnScreenBufEnd >= LINE_LINES_MAX)
      {
        gnScreenBufEnd = 0;
      }
      if (gnScreenBufEnd == gnScreenBufStart)
      {
        gnScreenBufStart++;
        if (gnScreenBufStart >= LINE_LINES_MAX)
        {
          gnScreenBufStart = 0;
        }
      }
    }
  }
  gszScreenBuf[gnScreenBufEnd][gnScreenBufChar] = 0;

//  if (nSize > (int)sizeof(gszCleanBuffer))
//  {
//    nCleanSize = CleanText (szText + nSize - (int)sizeof(gszCleanBuffer),
//      (int)sizeof(gszCleanBuffer), gszCleanBuffer,
//      (int)sizeof(gszCleanBuffer));
//  }
//  else
//  {
//    nCleanSize = CleanText (szText, nSize, gszCleanBuffer,
//      (int)sizeof(gszCleanBuffer));
//  }
//
//  if (nCleanSize >= ((int)sizeof(gszTextBuffer) - 1))
//  {
//    // Fill the entire buffer
//    memcpy (gszTextBuffer, szText + nCleanSize - (int)sizeof(gszTextBuffer),
//      (int)sizeof(gszTextBuffer));
//    gnBuffStart = (int)sizeof(gszTextBuffer) - 1;
//    gszTextBuffer[gnBuffStart] = 0;
//  }
//  else
//  {
//    // Add the text to the buffer
//    nCopyPos = 0;
//    nCopySize = (int)sizeof(gszTextBuffer) - gnBuffStart;
//    if (nCleanSize > nCopySize)
//    {
//      memcpy (gszTextBuffer + gnBuffStart, szText, nCopySize);
//      memcpy (gszTextBuffer, szText + nCopySize, nCleanSize - nCopySize);
//      gnBuffStart = nCleanSize - nCopySize;
//      if (gnBuffStart >= (int)sizeof(gszTextBuffer))
//      {
//        gnBuffStart = 0;
//      }
//      gszTextBuffer[gnBuffStart] = 0;
//    }
//    else
//    {
//      memcpy (gszTextBuffer + gnBuffStart, szText, nCleanSize);
//
//      gnBuffStart += nCleanSize;
//      if (gnBuffStart >= (int)sizeof(gszTextBuffer))
//      {
//        gnBuffStart = 0;
//      }
//      gszTextBuffer[gnBuffStart] = 0;
//    }
//  }

  // Redraw the window
  sState.w = gwhMain;
  xwimp_get_window_state (& sState);
  sRedraw.w = gwhMain;
  sRedraw.box = sState.visible;
  sRedraw.xscroll = sState.xscroll;
  sRedraw.yscroll = sState.yscroll;
  xwimp_redraw_window (& sRedraw, & boMore);
  while (boMore)
  {
    RedrawMain (& sRedraw);
    xwimp_get_rectangle (& sRedraw, & boMore);
  }
}

//////////////////////////////////////////////////////////////////
// Child has produced output
void TaskWindowOutput (wimp_block *pcBlock)
{
  char                  *szText;
  int                   nSize;
  int                   nSendSize;

  REPORT ("Output");
  REPORTVAR ("Handle %08x", (int)gnChildHandle);
  if (pcBlock->message.sender == gnChildHandle)
  {
    szText = (char*)(pcBlock->reserved + 24);
    nSize = *(int*)(pcBlock->reserved + 20);

    // Clean up the text
    if (nSize > gnLocalEcho)
    {
      nSendSize = CleanText (szText + gnLocalEcho, nSize - gnLocalEcho,
        gpcSendBuffer, (int)sizeof(gpcSendBuffer));

      if (nSendSize > 0)
      {
        // Print the text
        AddTextBlock (gpcSendBuffer, nSendSize);

        if (gnAcceptSocket != SOCKET_NOT_SET)
        {
          // Send the data over the network
          SocketSend (& gnAcceptSocket, gpcSendBuffer, nSendSize);
        }
      }
      gnLocalEcho = 0;
    }
    else
    {
      gnLocalEcho -= nSize;
    }
  }
}

//////////////////////////////////////////////////////////////////
// Inform about child's task-id
void TaskWindowEgo (wimp_block *pcBlock)
{
  REPORT ("Ego");
  REPORTVAR ("Txt handle %08x", *(int*)(pcBlock->reserved + 20));
  if (*(int*)(pcBlock->reserved + 20) == TASK_TEXT_HANDLE)
  {
    gnChildHandle = pcBlock->message.sender;
    gboChildStarted = TRUE;
    REPORTVAR ("Handle %08x", (int)gnChildHandle);
  }
}

//////////////////////////////////////////////////////////////////
// Child has exited
void TaskWindowMorio (wimp_block *pcBlock)
{
  REPORT ("Mario");
  REPORTVAR ("Handle %08x", (int)gnChildHandle);
  if (pcBlock->message.sender == gnChildHandle)
  {
    gnChildHandle = 0;
    gboChildStarted = FALSE;

    if (geState == STATE_ACCEPTED)
    {
      SetState (STATE_CLOSEACCEPTED);
    }
  }
}

//////////////////////////////////////////////////////////////////
// Start a task
void StartChildTask (void)
{
  char                  szTask[1024];

  if (!gboChildStarted)
  {
    gnChildHandle = 0;
    gboChildStarted = TRUE;

    sprintf (szTask,
      "*TaskWindow \"*\" -wimpslot 640k -name \"Telnet\" -task &%08x -txt &%08x",
      (unsigned int)gnTaskHandle, TASK_TEXT_HANDLE);

    REPORT (szTask);
    wimp_start_task (szTask);
    REPORT ("Started");
  }
  else
  {
    // Kill the child task
    KillChild ();
  }
}

//////////////////////////////////////////////////////////////////
// Kill the child task if it's active
void KillChild (void)
{
  wimp_message          sMessage;

  sMessage.your_ref = 0;
  sMessage.action = 0x808c4;
  sMessage.size = WORDALIGN(20);
  xwimp_send_message (wimp_USER_MESSAGE, & sMessage, gnChildHandle);
  REPORT ("Killed");
  gnChildHandle = 0;
  gboChildStarted = FALSE;
}

//////////////////////////////////////////////////////////////////
// General null poll stuff (mostly related to listening on a socket)
void NullPoll (void)
{
  SOCK                       eReturn;
  socket_sockaddr            sAddress;
  int                        nLen;
  int                        nTime;

  if (gePreviousState != geState)
  {
    REPORTVAR ("* State changed from %d", gePreviousState);
    REPORTVAR ("                  to %d", geState);
    gePreviousState = geState;
    xos_read_monotonic_time (& gnActionTime);
  }

  switch (geState)
  {
    case STATE_CLOSED:
      // Do nothing
      gnPollTime = POLLTIME_NONE;
      break;
    case STATE_CREATELISTENING:
      // Create a socket
      if (CreateSocket (& gnListenSocket) == SOCK_OK)
      {
        gboAcceptIncoming = TRUE;
        SetIconSelectionState (gboAcceptIncoming, gwhCont, 5);

        REPORT ("Created listening");
        SetState (STATE_CREATEDLISTENING);
        UpdateIconbarIcon ();
        gnPollTime = POLLTIME_FAST;
      }
      else
      {
        REPORT ("Listening creation failed");
        SetState (STATE_CLOSED);
        UpdateIconbarIcon ();
      }
      break;
    case STATE_CREATEDLISTENING:
      if (ListenSocket (& gnListenSocket) == SOCK_OK)
      {
        REPORT ("Listening");
        sprintf (gszPrintTemp, "\n\r%s\n\r", Tag ("MsgListen"));
        AddTextBlock (gszPrintTemp, strlen (gszPrintTemp));

        SetState (STATE_LISTENING);
        UpdateIconbarIcon ();
        gnPollTime = POLLTIME_LISTEN;
      }
      else
      {
        REPORT ("Listening failed");
        SetState (STATE_CLOSELISTENING);
        UpdateIconbarIcon ();
      }
      break;
    case STATE_LISTENING:
      eReturn = Listen (& gnListenSocket, & sAddress, & gnAcceptSocket);
      if (eReturn == SOCK_ERR)
      {
        UpdateIconbarIcon ();
        SetState (STATE_CLOSELISTENING);
      }
      if (eReturn == SOCK_RECEIVE)
      {
        REPORT ("Send username");
        gnUserPassPos = 0;

        nLen = sizeof(sAddress);
        xsocket_getpeername (gnAcceptSocket, & sAddress, & nLen);

        if (gboNotify)
        {
          sprintf (gszPrintTemp, TagArg ("MsgConn",
            inet_ntoa(*(struct in_addr*)(&sAddress.sockaddr_in.addr))));
          SetIconText (gszPrintTemp, gwhConn, 0);
          OpenWindowInitCentre (gwhConn);
        }

        sprintf (gszPrintTemp, "\n\r%s\n\r",
          TagArg ("MsgOpen",
          inet_ntoa(*(struct in_addr*)(&sAddress.sockaddr_in.addr))));
        AddTextBlock (gszPrintTemp, strlen (gszPrintTemp));
        sprintf (gszPrintTemp, "\n\r%s ", Tag ("MsgUser"));
        SocketSend (& gnAcceptSocket, gszPrintTemp, strlen (gszPrintTemp));

        SetState (STATE_LOGIN);
        UpdateIconbarIcon ();
        gnPollTime = POLLTIME_ACTIVE;
      }
      break;
    case STATE_LOGIN:
      eReturn = StateLogin (& gnAcceptSocket, & gnListenSocket);
      if (eReturn == SOCK_OK)
      {
        gnUserPassPos = 0;
        sprintf (gszPrintTemp, "%s \xff\xfb\x01", Tag ("MsgPass"));
        SocketSend (& gnAcceptSocket, gszPrintTemp, strlen (gszPrintTemp));
        SetState (STATE_PASSWORD);
        UpdateIconbarIcon ();
        gnPollTime = POLLTIME_ACTIVE;
      }
      if (eReturn == SOCK_ERR)
      {
        SetState (STATE_CLOSEACCEPTED);
      }
      break;
    case STATE_PASSWORD:
      eReturn = StatePassword (& gnAcceptSocket, & gnListenSocket);
      if (eReturn == SOCK_OK)
      {
        gnUserPassPos = 0;
        sprintf (gszPrintTemp, "\n\r%s\n\r",
          TagArg ("MsgStart", gszUserName));
        SocketSend (& gnAcceptSocket, gszPrintTemp, strlen (gszPrintTemp));

        sprintf (gszPrintTemp, "\n\r%s\n\r", Tag ("MsgWelcome"));
        AddTextBlock (gszPrintTemp, strlen(gszPrintTemp));

        StartChildTask ();

        SetState (STATE_ACCEPTED);
        UpdateIconbarIcon ();
        gnPollTime = POLLTIME_ACTIVE;
      }
      if ((eReturn == SOCK_FAIL) || (eReturn == SOCK_ERR))
      {
        SetState (STATE_CLOSEACCEPTED);
        UpdateIconbarIcon ();
      }
      break;
    case STATE_ACCEPTED:
      if (ReadSocket (& gnAcceptSocket, & gnListenSocket) == SOCK_ERR)
      {
        REPORT ("Error on accepted socket");
        SetState (STATE_CLOSEACCEPTED);
        UpdateIconbarIcon ();
      }
      break;
    case STATE_CLOSEACCEPTED:
      nLen = sizeof(sAddress);
      xsocket_getpeername (gnAcceptSocket, & sAddress, & nLen);

      if (CloseSocket (& gnAcceptSocket) == SOCK_OK)
      {
        sprintf (gszPrintTemp, "\n\r%s\n\r", Tag ("MsgClose"));
        AddTextBlock (gszPrintTemp, strlen(gszPrintTemp));
        REPORT ("Closed accepted");
        KillChild ();

        if (gboNotify)
        {
          sprintf (gszPrintTemp, TagArg ("MsgDisc",
            inet_ntoa(*(struct in_addr*)(&sAddress.sockaddr_in.addr))));
          SetIconText (gszPrintTemp, gwhConn, 0);
          OpenWindowInitCentre (gwhConn);
        }

        if (gboCloseAllConnections)
        {
          SetState (STATE_CLOSELISTENING);
          UpdateIconbarIcon ();
        }
        else
        {
          SetState (STATE_LISTENING);
          UpdateIconbarIcon ();
          gnPollTime = POLLTIME_LISTEN;
        }
      }
      break;
    case STATE_CLOSELISTENING:
      if (CloseSocket (& gnListenSocket) == SOCK_OK)
      {
        gboAcceptIncoming = FALSE;
        SetIconSelectionState (gboAcceptIncoming, gwhCont, 5);
        REPORT ("Closed listening");
        sprintf (gszPrintTemp, "\n\r%s\n\r", Tag ("MsgDeaf"));
        AddTextBlock (gszPrintTemp, strlen(gszPrintTemp));

        SetState (STATE_CLOSED);
        UpdateIconbarIcon ();
      }
      break;
    default:
      SetState (STATE_CLOSED);
      break;
  }

  if ((geState == STATE_LOGIN)
    || (geState == STATE_PASSWORD)
    || (geState == STATE_ACCEPTED))
  {
    if (gnTimeOut > 0)
    {
      xos_read_monotonic_time (& nTime);
      if (nTime > (gnActionTime + gnTimeOut))
      {
        // Timeout has occured
        REPORT ("Timeout");
        SetState (STATE_CLOSEACCEPTED);
      }
    }
  }
}

//////////////////////////////////////////////////////////////////
// Create Socket
SOCK CreateSocket (socket_s * pnSocket)
{
  socket_sockaddr            sAddress;
  int                        nArg;
  os_error                   *psError;
  SOCK                       eReturn;
//  char                       cArg;

  eReturn = SOCK_OK;

  psError = xsocket_creat (socket_AF_INET, socket_SOCK_STREAM,
    socket_IPPROTO_IP, pnSocket);
  if (psError)
  {
    err (psError);
    eReturn = SOCK_FAIL;
  }
  else
  {
    nArg = -1;
    psError = xsocket_setsockopt (*pnSocket, socket_SOL_SOCKET,
      socket_SO_REUSEADDR, (char *)& nArg, sizeof (nArg));

    if (psError)
    {
      err (psError);
      eReturn = SOCK_ERR;
    }
    else
    {
      sAddress.sockaddr.af = socket_AF_INET;
      sAddress.sockaddr_in.af = socket_AF_INET;
      sAddress.sockaddr_in.port = htons (gnPort);
      sAddress.sockaddr_in.addr = htonl (INADDR_ANY);

      REPORTVAR ("Socket: %d", (int)(*pnSocket));
      psError = xsocket_bind (*pnSocket, & sAddress, 16);

      if (psError)
      {
        err (psError);
        eReturn = SOCK_ERR;
      }
      /*
      else
      {
        cArg = 1;
        psError = xsocket_ioctl (*pnSocket, socket_FIONBIO, & cArg);

        if (psError)
        {
          err (psError);
          eReturn = SOCK_ERR;
        }
      }
      */
    }
  }
  return eReturn;
}

//////////////////////////////////////////////////////////////////
// Start listen for connections on a socket
SOCK ListenSocket (socket_s * pnSocket)
{
  os_error                   *psError;
  SOCK                       eReturn;

  eReturn = SOCK_OK;

  psError = xsocket_listen (*pnSocket, LISTEN_BACKLOG);

  if (psError)
  {
    err (psError);
    eReturn = SOCK_FAIL;
  }

  return eReturn;
}

//////////////////////////////////////////////////////////////////
// Close Socket
SOCK CloseSocket (socket_s * pnSocket)
{
  os_error                   *psError;
  SOCK                       eReturn;

  eReturn = SOCK_OK;

  psError = xsocket_close (*pnSocket);

  if (psError)
  {
    err (psError);
    eReturn = SOCK_FAIL;
  }
  else
  {
    *pnSocket = SOCKET_NOT_SET;
  }

  return eReturn;
}

//////////////////////////////////////////////////////////////////
// Start the socket stuff
void StartListening (void)
{
  switch (geState)
  {
    case STATE_CLOSED:
      SetState (STATE_CREATELISTENING);
      gboCloseAllConnections = FALSE;
      break;
    case STATE_LISTENING:
      SetState (STATE_CLOSELISTENING);
      break;
    case STATE_LOGIN:
    case STATE_PASSWORD:
    case STATE_ACCEPTED:
      SetState (STATE_CLOSEACCEPTED);
      break;
    default:
      // Do nothing
      break;
  }
}

//////////////////////////////////////////////////////////////////
// Actively listen for connections on a socket
SOCK Listen (socket_s * pnListen, socket_sockaddr * psAddress, socket_s * pnAccept)
{
  os_error                   *psError;
  SOCK                       eReturn;
  socket_fdset               sRead;
  socket_fdset               sWrite;
  socket_fdset               sExcept;
  int                        nFound;
  socket_timeval             sTimeVal;
  int                        nAddLen;
  int                        nArg;
  char                       cArg;

  eReturn = SOCK_WAIT;

  FD_ZERO (& sRead);
  FD_ZERO (& sWrite);
  FD_ZERO (& sExcept);

  FD_SET ((unsigned int)(*pnListen), (fd_set*)(& sRead));

  sTimeVal.sec = 0;
  sTimeVal.usec = 100;

  psError = xsocket_select (((unsigned int)(*pnListen)) + 1, & sRead,
    NULL, NULL, & sTimeVal, & nFound);

  if (psError)
  {
    REPORTVAR ("Listen error %d", psError->errnum);
    err (psError);
    eReturn = SOCK_ERR;
  }
  else
  {
    if (nFound > 0)
    {
      if (FD_ISSET ((unsigned int)(*pnListen), (fd_set*)(& sRead)))
      {
        // Accept the connection!
        REPORT ("Found on listening socket!");
        eReturn = SOCK_RECEIVE;

        if (psAddress)
        {
          nAddLen = sizeof (socket_sockaddr);
        }
        else
        {
          nAddLen = 0;
        }
        psError = xsocket_accept (*pnListen, psAddress, & nAddLen, pnAccept);

        if (psError)
        {
          REPORTVAR ("Listen accept error %d", psError->errnum);
          err (psError);
          eReturn = SOCK_ERR;
        }
        else
        {
          // Socket accepted!
          REPORT ("New connection accepted!");
          nArg = -1;
          psError = xsocket_setsockopt (*pnAccept, socket_SOL_SOCKET,
            socket_SO_REUSEADDR, (char *)& nArg, sizeof (nArg));

          if (psError)
          {
            err (psError);
            eReturn = SOCK_ERR;
          }
          else
          {
            cArg = 1;
            psError = xsocket_ioctl (*pnAccept, socket_FIONBIO, & cArg);

            if (psError)
            {
              err (psError);
              eReturn = SOCK_ERR;
            }
            else
            {
              eReturn = SOCK_RECEIVE;
            }
          }
        }
      }
    }
  }

  return eReturn;
}

//////////////////////////////////////////////////////////////////
// Deal with stuff arriving on the accepted socket
SOCK ReadSocket (socket_s * pnSocket, socket_s * pnListen)
{
  os_error                   *psError;
  SOCK                       eReturn;
  int                        nDataReceived;
  socket_fdset               sRead;
  socket_fdset               sWrite;
  socket_fdset               sExcept;
  int                        nFound;
  socket_timeval             sTimeVal;
  int                        nAddLen;
  unsigned int               uMaxSocket;
  socket_s                   nAccept;

  eReturn = SOCK_WAIT;

  FD_ZERO (& sRead);
  FD_ZERO (& sWrite);
  FD_ZERO (& sExcept);

  FD_SET ((unsigned int)(*pnListen), (fd_set*)(& sRead));
  FD_SET ((unsigned int)(*pnSocket), (fd_set*)(& sRead));

  sTimeVal.sec = 0;
  sTimeVal.usec = 1;

  if ((unsigned int)(*pnSocket) > (unsigned int)(*pnListen))
  {
    uMaxSocket = (unsigned int)(*pnSocket) + 1;
  }
  else
  {
    uMaxSocket = (unsigned int)(*pnListen) + 1;
  }

  psError = xsocket_select (uMaxSocket, & sRead, NULL, NULL,
    & sTimeVal, & nFound);

  if (psError)
  {
    REPORTVAR ("Listen error %d", psError->errnum);
    err (psError);
  }
  else
  {
    if (nFound > 0)
    {
      if (FD_ISSET ((unsigned int)(*pnListen), (fd_set*)(& sRead)))
      {
        // Don't accept the connection!
        REPORT ("Refused on listening socket!");
        nAddLen = 0;
        psError = xsocket_accept (*pnListen, NULL, & nAddLen, & nAccept);

        if (psError)
        {
          REPORTVAR ("Refused listen accept error %d", psError->errnum);
          err (psError);
        }
        else
        {
          xsocket_close (nAccept);
        }
      }

      if (FD_ISSET ((unsigned int)(*pnSocket), (fd_set*)(& sRead)))
      {
        ///////////////////////////////////////////////
        // Read on bound socket
        ///////////////////////////////////////////////
        psError = xsocket_read (*pnSocket, gpcBuffer, sizeof (gpcBuffer),
          & nDataReceived);

        if (!psError)
        {
          if (nDataReceived == 0)
          {
            SetState (STATE_CLOSEACCEPTED);
          }
          else
          {
            SendToChild (gpcBuffer, nDataReceived);
//            AddTextBlock (gpcBuffer, nDataReceived);
            xos_read_monotonic_time (& gnActionTime);
          }
        }
        else
        {
          if ((psError->errnum != socket_EWOULDBLOCK)
            && (psError->errnum != (socket_EWOULDBLOCK + IYONIX_SOCKET_ERR))
            && (psError->errnum != socket_EINPROGRESS)
            && (psError->errnum != (socket_EINPROGRESS + IYONIX_SOCKET_ERR)))
          {
            err (psError);
            eReturn = SOCK_ERR;
          }
        }
      }
    }
  }

  return eReturn;
}

//////////////////////////////////////////////////////////////////
// Send data on the socket
SOCK SocketSend (socket_s * pnSocket, char * szText, int nSize)
{
  os_error                   *psError;
  SOCK                       eReturn;
  int                        nSent;

  eReturn = SOCK_WAIT;
  psError = xsocket_send (*pnSocket, szText, nSize, 0x40, & nSent);
  xos_read_monotonic_time (& gnActionTime);

  if (psError)
  {
    if ((psError->errnum != socket_EWOULDBLOCK)
      && (psError->errnum != (socket_EWOULDBLOCK + IYONIX_SOCKET_ERR))
      && (psError->errnum != socket_EINPROGRESS)
      && (psError->errnum != (socket_EINPROGRESS + IYONIX_SOCKET_ERR)))
    {
      err (psError);
      eReturn = SOCK_ERR;
    }
  }


  return eReturn;
}

//////////////////////////////////////////////////////////////////
// Send data to child task
void SendToChild (char * szText, int nSize)
{
  wimp_message                sMessage;
  int                         nSendSize;

  if (nSize > 232)
  {
    // This needs sorting out properly
    nSize = 232;
  }

  nSendSize = CleanReceived (szText, nSize, gpcCleanReceived,
    (int)sizeof(gpcCleanReceived));

  if (nSendSize > 0)
  {
    sMessage.size = WORDALIGN ((24 + nSendSize));
    sMessage.your_ref = 0;
    sMessage.action = 0x808c0;
    *(int*)(((char*)(&sMessage)) + 20) = nSendSize;
    memcpy ((((char*)(&sMessage)) + 24), gpcCleanReceived, nSendSize);

    // The following pair of lines should be swapped of
    // we have local echo set
//    gnLocalEcho += nSendSize;
    gnLocalEcho = 0;
    err (xwimp_send_message (wimp_USER_MESSAGE, & sMessage, gnChildHandle));
  }
}

//////////////////////////////////////////////////////////////////
// Clean up the text and copy it to a new buffer
int CleanText (char * szFrom, int nFromSize, char * szTo, int nToSize)
{
  int                         nPosFrom;
  int                         nPosTo;

  nPosFrom = 0;
  nPosTo = 0;
  while ((nPosFrom < nFromSize) && (nPosTo < nToSize))
  {
    if (szFrom[nPosFrom] < 0x20)
    {
      switch (szFrom[nPosFrom])
      {
        case 0x0a:
          if (((nPosFrom + 1) < nFromSize) && (szFrom[nPosFrom + 1] == 0x0d))
          {
            nPosFrom++;
          }

          szTo[nPosTo] = 0x0a;
          nPosTo++;
          if (nPosTo < nToSize)
          {
            szTo[nPosTo] = 0x0d;
            nPosTo++;
          }
          break;
        case 0x0d:
          if (((nPosFrom + 1) < nFromSize) && (szFrom[nPosFrom + 1] == 0x0a))
          {
            nPosFrom++;
          }

          szTo[nPosTo] = 0x0a;
          nPosTo++;
          if (nPosTo < nToSize)
          {
            szTo[nPosTo] = 0x0d;
            nPosTo++;
          }
          break;
        case 0x08:
        case 0x09:
          szTo[nPosTo] = szFrom[nPosFrom];
          nPosTo++;
          break;
        case 0x07:
        case 0x11:
          nPosFrom += 1;
          break;
        case 0x12:
        case 0x16:
        case 0x1f:
          nPosFrom += 2;
          break;
        case 0x17:
        case 0x19:
          nPosFrom += 9;
          break;
        case 0x1c:
          nPosFrom += 4;
          break;
        case 0x1d:
          nPosFrom += 8;
          break;
        case 0x18:
          nPosFrom += 16;
          break;
        default:
          // Do nothing
          break;
      }
    }
    else
    {
      szTo[nPosTo] = szFrom[nPosFrom];
      nPosTo++;
    }
    nPosFrom++;
  }
  return nPosTo;
}

//////////////////////////////////////////////////////////////////
// Clean up the text to be sent to the child and copy it to a new buffer
int CleanReceived (char * szFrom, int nFromSize, char * szTo, int nToSize)
{
  int                         nPosFrom;
  int                         nPosTo;

  nPosFrom = 0;
  nPosTo = 0;
  while ((nPosFrom < nFromSize) && (nPosTo < nToSize))
  {
    if ((nPosFrom < (nFromSize - 1))
      && (((szFrom[nPosFrom] == 0x0a)
      && (szFrom[nPosFrom + 1] == 0x0d))
      || ((szFrom[nPosFrom] == 0x0d)
      && (szFrom[nPosFrom + 1] == 0x0a))))
    {
      szTo[nPosTo] = szFrom[nPosFrom];
      nPosFrom++;
    }
    else
    {
      if (szFrom[nPosFrom] == 0xff)
      {
        nPosFrom += 2;
      }
      else
      {
        szTo[nPosTo] = szFrom[nPosFrom];
      }
    }
    nPosTo++;
    nPosFrom++;
  }

  return nPosTo;
}

//////////////////////////////////////////////////////////////////
// Get the user login name
SOCK StateLogin (socket_s * pnSocket, socket_s * pnListen)
{
  os_error                   *psError;
  SOCK                       eReturn;
  int                        nDataReceived;
  socket_fdset               sRead;
  socket_fdset               sWrite;
  socket_fdset               sExcept;
  int                        nFound;
  socket_timeval             sTimeVal;
  int                        nAddLen;
  unsigned int               uMaxSocket;
  socket_s                   nAccept;

  eReturn = SOCK_WAIT;

  FD_ZERO (& sRead);
  FD_ZERO (& sWrite);
  FD_ZERO (& sExcept);

  FD_SET ((unsigned int)(*pnListen), (fd_set*)(& sRead));
  FD_SET ((unsigned int)(*pnSocket), (fd_set*)(& sRead));

  sTimeVal.sec = 0;
  sTimeVal.usec = 1;

  if ((unsigned int)(*pnSocket) > (unsigned int)(*pnListen))
  {
    uMaxSocket = (unsigned int)(*pnSocket) + 1;
  }
  else
  {
    uMaxSocket = (unsigned int)(*pnListen) + 1;
  }

  psError = xsocket_select (uMaxSocket, & sRead, NULL, NULL,
    & sTimeVal, & nFound);

  if (psError)
  {
    REPORTVAR ("Listen error %d", psError->errnum);
    err (psError);
  }
  else
  {
    if (nFound > 0)
    {
      if (FD_ISSET ((unsigned int)(*pnListen), (fd_set*)(& sRead)))
      {
        // Don't accept the connection!
        REPORT ("Refused on listening socket!");
        nAddLen = 0;
        psError = xsocket_accept (*pnListen, NULL, & nAddLen, & nAccept);

        if (psError)
        {
          REPORTVAR ("Refused listen accept error %d", psError->errnum);
          err (psError);
        }
        else
        {
          xsocket_close (nAccept);
        }
      }

      if (FD_ISSET ((unsigned int)(*pnSocket), (fd_set*)(& sRead)))
      {
        ///////////////////////////////////////////////
        // Read on bound socket
        ///////////////////////////////////////////////
        if (gnUserPassPos < (int)sizeof(gszUsernameTemp))
        {
          psError = xsocket_read (*pnSocket, gszUsernameTemp + gnUserPassPos,
            sizeof (gszUsernameTemp) - gnUserPassPos,
            & nDataReceived);
        }
        else
        {
          psError = NULL;
          nDataReceived = 0;
        }

        if (!psError)
        {
          if (nDataReceived == 0)
          {
            SetState (STATE_CLOSEACCEPTED);
          }
          else
          {
            xos_read_monotonic_time (& gnActionTime);
            if (gszUsernameTemp[gnUserPassPos] != 0xff)
            {
              gnUserPassPos += nDataReceived;
              // Terminate the username
              if (gszUsernameTemp[gnUserPassPos - 1] < 0x20)
              {
                while ((gnUserPassPos > 1)
                  && (gszUsernameTemp[gnUserPassPos - 1] < 0x20))
                {
                  gnUserPassPos--;
                }
                gszUsernameTemp[gnUserPassPos] = 0;

                eReturn = SOCK_OK;
              }
            }
          }
        }
        else
        {
          if ((psError->errnum != socket_EWOULDBLOCK)
            && (psError->errnum != (socket_EWOULDBLOCK + IYONIX_SOCKET_ERR))
            && (psError->errnum != socket_EINPROGRESS)
            && (psError->errnum != (socket_EINPROGRESS + IYONIX_SOCKET_ERR)))
          {
            err (psError);
            eReturn = SOCK_ERR;
          }
        }
      }
    }
  }
  return eReturn;
}

//////////////////////////////////////////////////////////////////
// Get the user password
SOCK StatePassword (socket_s * pnSocket, socket_s * pnListen)
{
  os_error                   *psError;
  SOCK                       eReturn;
  int                        nDataReceived;
  socket_fdset               sRead;
  socket_fdset               sWrite;
  socket_fdset               sExcept;
  int                        nFound;
  socket_timeval             sTimeVal;
  int                        nAddLen;
  unsigned int               uMaxSocket;
  socket_s                   nAccept;

  eReturn = SOCK_WAIT;

  FD_ZERO (& sRead);
  FD_ZERO (& sWrite);
  FD_ZERO (& sExcept);

  FD_SET ((unsigned int)(*pnListen), (fd_set*)(& sRead));
  FD_SET ((unsigned int)(*pnSocket), (fd_set*)(& sRead));

  sTimeVal.sec = 0;
  sTimeVal.usec = 1;

  if ((unsigned int)(*pnSocket) > (unsigned int)(*pnListen))
  {
    uMaxSocket = (unsigned int)(*pnSocket) + 1;
  }
  else
  {
    uMaxSocket = (unsigned int)(*pnListen) + 1;
  }

  psError = xsocket_select (uMaxSocket, & sRead, NULL, NULL,
    & sTimeVal, & nFound);

  if (psError)
  {
    REPORTVAR ("Listen error %d", psError->errnum);
    err (psError);
  }
  else
  {
    if (nFound > 0)
    {
      if (FD_ISSET ((unsigned int)(*pnListen), (fd_set*)(& sRead)))
      {
        // Don't accept the connection!
        REPORT ("Refused on listening socket!");
        nAddLen = 0;
        psError = xsocket_accept (*pnListen, NULL, & nAddLen, & nAccept);

        if (psError)
        {
          REPORTVAR ("Refused listen accept error %d", psError->errnum);
          err (psError);
        }
        else
        {
          xsocket_close (nAccept);
        }
      }

      if (FD_ISSET ((unsigned int)(*pnSocket), (fd_set*)(& sRead)))
      {
        ///////////////////////////////////////////////
        // Read on bound socket
        ///////////////////////////////////////////////
        if (gnUserPassPos < (int)sizeof(gszPasswordTemp))
        {
          psError = xsocket_read (*pnSocket, gszPasswordTemp + gnUserPassPos,
            sizeof (gszPasswordTemp) - gnUserPassPos,
            & nDataReceived);
        }
        else
        {
          psError = NULL;
          nDataReceived = 0;
        }

        if (!psError)
        {
          if (nDataReceived == 0)
          {
            SetState (STATE_CLOSEACCEPTED);
          }
          else
          {
            xos_read_monotonic_time (& gnActionTime);
            if (gszPasswordTemp[gnUserPassPos] != 0xff)
            {
              gnUserPassPos += nDataReceived;
              // Terminate the password
              if (gszPasswordTemp[gnUserPassPos - 1] < 0x20)
              {
                while ((gnUserPassPos > 1)
                  && (gszPasswordTemp[gnUserPassPos - 1] < 0x20))
                {
                  gnUserPassPos--;
                }
                gszPasswordTemp[gnUserPassPos] = 0;

                if (CheckUserDetails (gszUsernameTemp, gszPasswordTemp))
                {
                  eReturn = SOCK_OK;
                }
                else
                {
                  eReturn = SOCK_FAIL;
                }
              }
            }
          }
        }
        else
        {
          if ((psError->errnum != socket_EWOULDBLOCK)
            && (psError->errnum != (socket_EWOULDBLOCK + IYONIX_SOCKET_ERR))
            && (psError->errnum != socket_EINPROGRESS)
            && (psError->errnum != (socket_EINPROGRESS + IYONIX_SOCKET_ERR)))
          {
            err (psError);
            eReturn = SOCK_ERR;
          }
        }
      }
    }
  }

  return eReturn;
}

//////////////////////////////////////////////////////////////////
// Set the sprite string for an icon
void SetIconSprite (char * szSprite, 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 ((char*)(sIconState.icon.data.indirected_sprite.id), szSprite);
  }
  else
  {
    strncpy (sIconState.icon.data.sprite, szSprite, 12);
    sIconState.icon.data.sprite[11] = 0;
  }

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

//////////////////////////////////////////////////////////////////
// Set the iconbar icon details
void UpdateIconbarIcon (void)
{
  char                        szIconName[32];
  bool                        boHidden;

  strcpy (szIconName, Tag ("IcnDeaf"));
  boHidden = FALSE;

  switch (geState)
  {
    default:
    case STATE_CLOSED:
    case STATE_CREATELISTENING:
      if (gboHideIcon)
      {
        boHidden = TRUE;
      }
      else
      {
        strncpy (szIconName, Tag ("IcnDeaf"), sizeof (szIconName));
        boHidden = FALSE;
      }
      break;
    case STATE_CREATEDLISTENING:
    case STATE_LISTENING:
    case STATE_CLOSELISTENING:
      if (gboHideIcon)
      {
        boHidden = TRUE;
      }
      else
      {
        strncpy (szIconName, Tag ("IcnListen"), sizeof (szIconName));
        boHidden = FALSE;
      }
      break;
    case STATE_LOGIN:
    case STATE_PASSWORD:
    case STATE_ACCEPTED:
    case STATE_CLOSEACCEPTED:
      strncpy (szIconName, Tag ("IcnConnect"), sizeof (szIconName));
      boHidden = FALSE;
      break;
  }

  if (boHidden)
  {
    if (gboIconCreated)
    {
      DeleteIconbarIcon (gihIconBarIcon);
      gboIconCreated = FALSE;
    }
  }
  else
  {
    if (gboIconCreated)
    {
      SetIconSprite (szIconName, wimp_ICON_BAR_RIGHT,
        gihIconBarIcon);
    }
    else
    {
      gihIconBarIcon = CreateIconbarIconSprites (szIconName, 74, 58,
        gpcSprites);
      gboIconCreated = TRUE;
    }
  }
}

//////////////////////////////////////////////////////////////////
// Encode an MD5 digest for the given data (pcOut is 16 bytes)
void EncodeMD5 (char * pcIn, int nInSize, char * pcOut)
{
  struct MD5Context           sContext;

  MD5Init (& sContext);
  MD5Update (& sContext, pcIn, nInSize);
  MD5Final (pcOut, & sContext);
}

//////////////////////////////////////////////////////////////////
// Encode an hmac-MD5 digest for the given data (pcOut is 16 bytes)
void EncodeHMACMD5 (char * pcIn, int nInSize, char * pcKey, int nKeySize, char * pcOut)
{
  struct MD5Context           sContext;
//  char                        acKeyHashed[16];
  char                        acInPad[65];
  char                        acOutPad[65];
  int                         nPos;

  // if the key length is too long, MD5 hash it
  // or in this case, simply truncate it
  if (nKeySize > 64)
  {
//    EncodeMD5 (pcKey, nKeySize, acKeyHashed);
//    nKeySize = 16;
//    memcpy (pcKey, acKeyHashed, nKeySize);
    nKeySize = 64;
  }

  for (nPos = 0; nPos < 65; nPos++)
  {
    acInPad[nPos] = 0;
    acOutPad[nPos] = 0;
  }
  memcpy (acInPad, pcKey, nKeySize);
  memcpy (acOutPad, pcKey, nKeySize);

  for (nPos = 0; nPos < 64; nPos++)
  {
    acInPad[nPos] ^= 0x36;
    acOutPad[nPos] ^= 0x5c;
  }

  MD5Init (& sContext);
  MD5Update (& sContext, acInPad, 64);
  MD5Update (& sContext, pcIn, nInSize);
  MD5Final (pcOut, & sContext);

  MD5Init (& sContext);
  MD5Update (& sContext, acOutPad, 64);
  MD5Update (& sContext, pcOut, 16);
  MD5Final (pcOut, & sContext);
}

//////////////////////////////////////////////////////////////////
// Set choices details from dialogue entries
void SetChoicesDetails (void)
{
  char                        szPassword[PASSWORD_MAX];

  strncpy (gszUserName, GetIconText (gwhCont, 9), sizeof (gszUserName));

  strncpy (szPassword, GetIconText (gwhCont, 10), sizeof (szPassword));
  if (strlen (szPassword) > 0)
  {
    EncodeMD5 (szPassword, strlen (szPassword), gpcPassword);
  }

  if (GetIconSelectionState (gwhCont, 5))
  {
    gboCloseAllConnections = FALSE;
    if (geState == STATE_CLOSED)
    {
      SetState (STATE_CREATELISTENING);
    }
  }
  else
  {
    CloseAllConnections ();
  }

  gboAcceptIncoming = GetIconSelectionState (gwhCont, 5);
  gboNotify = GetIconSelectionState (gwhCont, 6);
  gboHideIcon = GetIconSelectionState (gwhCont, 7);
  sscanf (GetIconText (gwhCont, 8), "%d", & gnPort);
  sscanf (GetIconText (gwhCont, 18), "%d", & gnTimeOut);
  gnTimeOut = gnTimeOut * 100;

  UpdateIconbarIcon ();
}

//////////////////////////////////////////////////////////////////
// Set choices dialogue from details entries
void SetChoicesDialogue (void)
{
  char                        szString[STRING_LEN];

  SetIconText (gszUserName, gwhCont, 9);
  SetIconText ("", gwhCont, 10);

  SetIconSelectionState (gboAcceptIncoming, gwhCont, 5);
  SetIconSelectionState (gboNotify, gwhCont, 6);
  SetIconSelectionState (gboHideIcon, gwhCont, 7);
  sprintf (szString, "%d", gnPort);
  SetIconText (szString, gwhCont, 8);
  sprintf (szString, "%d", (gnTimeOut / 100));
  SetIconText (szString, gwhCont, 18);
}

//////////////////////////////////////////////////////////////////
// Open choices dialogue
void OpenChoicesDialogue (void)
{
  SetChoicesDialogue ();
  OpenWindowInitCentre (gwhCont);
  xwimp_set_caret_position (gwhCont, (wimp_i)8, 0, 0, -1, -1);
}

//////////////////////////////////////////////////////////////////
// Check username and password
bool CheckUserDetails (char * szUserName, char * szPassword)
{
  bool                        boReturn;
  char                        pcPassword[16];
  int                         nPos;

  boReturn = TRUE;

  if (strcmp (szUserName, gszUserName) != 0)
  {
    boReturn = FALSE;
  }

  EncodeMD5 (szPassword, strlen (szPassword), pcPassword);
  for (nPos = 0; nPos < 16; nPos++)
  {
    if (pcPassword[nPos] != gpcPassword[nPos])
    {
      boReturn = FALSE;
    }
    nPos++;
  }

  return boReturn;
}

//////////////////////////////////////////////////////////////////
// Close all connections
void CloseAllConnections (void)
{
  gboCloseAllConnections = TRUE;

  switch (geState)
  {
    case STATE_LISTENING:
      SetState (STATE_CLOSELISTENING);
      break;
    case STATE_LOGIN:
    case STATE_PASSWORD:
    case STATE_ACCEPTED:
      SetState (STATE_CLOSEACCEPTED);
      break;
    case STATE_CLOSED:
    default:
      // Do nothing
      break;
  }
}

//////////////////////////////////////////////////////////////////
// Save choices details
void SaveChoicesDetails (void)
{
  MemFile                     *pfhFile;
  char                        szString[STRING_LEN];
  fileswitch_object_type      eType;

  // Check that the directory exists
  xosfile_read_no_path (USER_SAVE_DIR, & eType, NULL, NULL, NULL, NULL);
  if (eType == fileswitch_NOT_FOUND)
  {
    // Create a directory
    xosfile_create_dir (USER_SAVE_DIR, 0);
  }

  pfhFile = memopen (USER_SAVE, "w");

  if (pfhFile)
  {
    memputs (Tag ("File"), pfhFile);

    SaveSectionMem (pfhFile, "General");
    sprintf (szString, "%d", gboAcceptIncoming);
    SaveDetailMem (pfhFile, "Accept", szString);
    sprintf (szString, "%d", gboNotify);
    SaveDetailMem (pfhFile, "Notify", szString);
    sprintf (szString, "%d", gboHideIcon);
    SaveDetailMem (pfhFile, "HideIcon", szString);
    sprintf (szString, "%d", gnPort);
    SaveDetailMem (pfhFile, "Port", szString);
    sprintf (szString, "%d", gnTimeOut);
    SaveDetailMem (pfhFile, "TimeOut", szString);

    SaveSectionMem (pfhFile, "User");
    SaveDetailMem (pfhFile, "Username", gszUserName);
    HexToString (szString, (int*)gpcPassword, 4);
    SaveDetailMem (pfhFile, "Password", szString);

    memclose (pfhFile);
  }
}

//////////////////////////////////////////////////////////////////
// Load choices details
bool LoadChoicesDetails (void)
{
  char                        szString[STRING_LEN];
  int                         nSectionPos;
  int                         nLen;
  MemFile                     *pfhFile;
  bool                        boReturn = TRUE;

  pfhFile = memopen (USER_LOAD, "r");

  if (pfhFile)
  {
    nLen = strlen (Tag ("File"));
    memgets (szString, nLen + 1, pfhFile);
    if (strncmp (szString, Tag ("File"), nLen) == 0)
    {
      nSectionPos = FindSectionMem (pfhFile, "General");
      FindValueMem (pfhFile, nSectionPos, "Accept",
        szString, sizeof (szString));
      sscanf (szString, "%d", & gboAcceptIncoming);
      FindValueMem (pfhFile, nSectionPos, "Notify",
        szString, sizeof (szString));
      sscanf (szString, "%d", & gboNotify);
      FindValueMem (pfhFile, nSectionPos, "HideIcon",
        szString, sizeof (szString));
      sscanf (szString, "%d", & gboHideIcon);
      FindValueMem (pfhFile, nSectionPos, "Port",
        szString, sizeof (szString));
      sscanf (szString, "%d", & gnPort);
      FindValueMem (pfhFile, nSectionPos, "TimeOut",
        szString, sizeof (szString));
      sscanf (szString, "%d", & gnTimeOut);

      nSectionPos = FindSectionMem (pfhFile, "User");
      FindValueMem (pfhFile, nSectionPos, "Username",
        gszUserName, sizeof (gszUserName));

      FindValueMem (pfhFile, nSectionPos, "Password",
        szString, sizeof (szString));
      StringToHex (szString, (int*)gpcPassword, 4);
    }
    else
    {
      boReturn = FALSE;
    }

    memclose (pfhFile);
  }

  return boReturn;
}

//////////////////////////////////////////////////////////////////
// Task initialise message
void TaskInitialise (wimp_block *pcBlock)
{
  int                         nPos;
  char                        *szTaskName;
  wimp_message                sMessage;

  REPORT ("Task initialise");
  szTaskName = (char*)(pcBlock->message.data.reserved + 8);

  nPos = 0;
  while ((szTaskName[nPos] == gszProgTitle[nPos])
    && (szTaskName[nPos] > 0x20))
  {
    nPos++;
  }
  if ((szTaskName[nPos] < 0x20) && (gszProgTitle[nPos] < 0x20))
  {
    // This is a task with the same name as us, but maybe it *is* us!
    if (pcBlock->message.sender != gnTaskHandle)
    {
      // No, it's a doppelganger
      sMessage.size = 20;
      sMessage.your_ref = 0;
      sMessage.action = 0;
      xwimp_send_message (wimp_USER_MESSAGE,
        & sMessage, pcBlock->message.sender);
      REPORT ("Quit Message sent");

      // Recreate our iconbar icon
      gboHideIcon = FALSE;
      UpdateIconbarIcon ();
    }
  }
}

//////////////////////////////////////////////////////////////////
// Save was selected from the console window menu
void SaveMenuSelect (void)
{
  char                        *szFilename;
  wimp_pointer                sPointer;

  xwimp_get_pointer_info (& sPointer);

  szFilename = GetIconText (gwhSave, 2);

  if (strchr(szFilename, '.'))
  {
    // Save the file
    SaveFile (szFilename);
  }
  else
  {
    OpenMenu ((wimp_menu *)gwhSave, sPointer.pos.x - 64, sPointer.pos.y);
  }
}

//////////////////////////////////////////////////////////////////
// Clear the console window
void ClearConsole (void)
{
  wimp_draw                   sRedraw;
  wimp_window_state           sState;
  bool                        boMore;
  int                         nCount;

  for (nCount = 0; nCount < LINE_LINES_MAX; nCount++)
  {
    gszScreenBuf[nCount][0] = 0;
  }
  gnScreenBufStart = 0;
  gnScreenBufEnd = 0;
  gnScreenBufChar = 0;

  // Redraw the window
  sState.w = gwhMain;
  xwimp_get_window_state (& sState);
  sRedraw.w = gwhMain;
  sRedraw.box = sState.visible;
  sRedraw.xscroll = sState.xscroll;
  sRedraw.yscroll = sState.yscroll;
  xwimp_redraw_window (& sRedraw, & boMore);
  while (boMore)
  {
    RedrawMain (& sRedraw);
    xwimp_get_rectangle (& sRedraw, & boMore);
  }
}

//////////////////////////////////////////////////////////////////
// Change the state
void SetState (STATE eState)
{
  REPORTVAR ("geState changed from %d", geState);
  gnPollTime = POLLTIME_FAST;
  geState = eState;
  REPORTVAR ("                  to %d", geState);
}
