/* Copyright 1997 Acorn Computers Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/***************************************************/
/* File   : Protocols.c                            */
/*                                                 */
/* Purpose: Handle some common protocol stuff,     */
/*          such as inter-application data         */
/*          transfer.                              */
/*                                                 */
/*          Function naming convention is as usual */
/*          a source leafname derived prefix, then */
/*          a code based on the protocol being     */
/*          dealt with:                            */
/*                                                 */
/*          _atats_  App To App Transfer, Save     */
/*          _atatl_  App To App Transfer, Load     */
/*          _pp_     Printer Protocol              */
/*          _auh_    Acorn URI Handler             */
/*          _ih_     Interactive Help              */
/*          _aub_    ANT URL Broadcast             */
/*          _multi_  Message is used in more than  */
/*                   one message protocol scheme   */
/*          _util_   A utility function not        */
/*                   directly connected with a     */
/*                   specific protocol.            */
/*                                                 */
/*          This is followed by the direction, so  */
/*          to speak - i.e. '_got_' for got a      */
/*          message, '_send_' for sending a        */
/*          message. Alternatively, a prefix       */
/*          '_bounced' is used for messages which  */
/*          return as a UserMessage_Acknowledge    */
/*          event.                                 */
/*                                                 */
/*          Because the Plug-In message protocol   */
/*          is such a large and self-contained     */
/*          entity, this is kept separate, in      */
/*          PlugIn.c.                              */
/*                                                 */
/* Author : A.D.Hodgkinson                         */
/*                                                 */
/* History: 29-Aug-97: Created.                    */
/*                                                 */
/*          06-Sep-97: Significant rewriting to    */
/*                     stop various clashes and    */
/*                     increase flexibility.       */
/***************************************************/

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

#include "swis.h"
#include "kernel.h"

#include "URI.h" /* URI handler API, in URILib:h */

#include "wimp.h"
#include "wimplib.h"
#include "event.h"

#include "toolbox.h"

#include "svcprint.h"
#include "Global.h"
#include "MiscDefs.h"
#include "Utils.h"

#include "Browser.h"
#include "FetchPage.h"
#include "Filetypes.h"
#include "History.h"
#include "Hotlist.h"
#include "Images.h"
#include "MimeMap.h"
#include "Printing.h"
#include "Save.h"
#include "SaveDraw.h"
#include "SaveFile.h"
#include "SaveObject.h"
#include "SaveText.h"
#include "Toolbars.h"
#include "URLutils.h"
#include "Windows.h"

#include "Protocols.h"

/* Conditional includes */

#ifdef TRACE
  #include "Trace.h"
#endif

/* Local definitions */

#define RTB_Size 4096 /* RAM transfer buffer size, in bytes */

/* Local variables */

/* Need to retain information across function calls about the */
/* different processes going on. Since an object save could   */
/* be in progress whilst other saves go on above it, all of   */
/* the remembered items have to be independent. Consequently, */
/* a lot of separate statics are needed.                      */

/* View source - broadcast loading */

static int            data_open_reference                = 0;

/* Saving the page source */

static int            save_source_reference              = 0;
static int            save_source_transmitted            = 0;
static browser_data * save_source_browser                = NULL;

/* Exporting the page as text */

static int            save_as_text_reference             = 0;
static browser_data * save_as_text_browser               = NULL;

/* Exporting the page as Draw */

static int            save_as_draw_reference             = 0;
static browser_data * save_as_draw_browser               = NULL;
static int            save_as_draw_backgrounds           = 0;

/* Saving a link */

static int            save_link_reference                = 0;
static int            save_link_transmitted              = 0;
static browser_data * save_link_browser                  = NULL;
static HStream      * save_link_token                    = NULL;
static int            save_link_as_url                   = 0;

/* Saving the current location */

static int            save_location_reference            = 0;
static int            save_location_transmitted          = 0;
static browser_data * save_location_browser              = NULL;
static int            save_location_as_url               = 0;

#ifndef REMOTE_HOTLIST

  /* Saving a hotlist item */

  static int            save_hotlist_entry_reference     = 0;
  static int            save_hotlist_entry_transmitted   = 0;
  static hotlist_item * save_hotlist_entry_item          = NULL;
  static int            save_hotlist_entry_as_url        = 0;

  /* Saving a hotlist selection */

  static int            save_hotlist_selection_reference   = 0;
  static int            save_hotlist_selection_transmitted = 0;

  /* Saving the entire hotlist */

  static int            save_entire_hotlist_reference      = 0;
  static int            save_entire_hotlist_transmitted    = 0;

#endif

/* Saving the History */

static int            save_history_reference             = 0;
static browser_data * save_history_browser               = NULL;

/* Saving an object through the fetcher */

static int            save_object_reference              = 0;
static int            save_object_transmitted            = 0;
static browser_data * save_object_browser                = NULL;
static int            save_object_through_scrap          = 0;

/* Exporting an image as a sprite */

static int            save_image_reference               = 0;
static int            save_image_transmitted             = 0;
static browser_data * save_image_browser                 = NULL;
static HStream      * save_image_token                   = NULL;
static int            save_image_as_original             = 0;

/* Loading data. For RAM transfer, buffers must be */
/* visible to all functions so they can be freed   */
/* in the event of a transfer failure.             */

static WimpMessage  * pending_data_save_ack              = NULL;
static char         * data_save_suggested_leafname       = NULL;

static void         * rtb                                = NULL;
static int            ram_fetch_reference                = 0;

static char         * ram_load_uri_buffer                = NULL;
static int            ram_load_buffer_size               = 0;
static int            ram_load_started                   = 0;

/* Printing */

static int            print_save_reference               = 0;

/* Static function prototypes */

static _kernel_oserror * protocols_atats_got_data_save_ack (WimpMessage * m);
static _kernel_oserror * protocols_pp_got_data_save_ack    (WimpMessage * m);

static void              protocols_util_update_reference   (int old_ref, int new_ref);

/*************************************************/
/* protocols_atats_send_data_open()              */
/*                                               */
/* Broadcasts a Message_DataOpen for the given   */
/* filetype and given pathname.                  */
/*                                               */
/* Parameters: The filetype to use;              */
/*                                               */
/*             Pointer to a null-terminated      */
/*             pathname to use.                  */
/*************************************************/

_kernel_oserror * protocols_atats_send_data_open(int filetype, char * pathname)
{
  WimpMessage dop;

  #ifdef TRACE
    if (tl & (1u<<28)) Printf("protocols_atats_send_data_open: Called\n");
  #endif

  /* Fill in the header */

  dop.hdr.your_ref    = 0;
  dop.hdr.action_code = Wimp_MDataOpen;

  /* Fill in the message body */

  dop.data.data_open.window_handle = 0;
  dop.data.data_open.x             = 0;
  dop.data.data_open.y             = 0;
  dop.data.data_open.file_type     = filetype;

  StrNCpy0(dop.data.data_open.path_name, pathname);

  dop.hdr.size = (int) WordAlign(strlen(dop.data.data_open.path_name) + 45); /* (44 for stuff up to the pathname, plus 1 for terminator) */

  /* Send the message */

  RetError(wimp_send_message(Wimp_EUserMessageRecorded, &dop, 0, 0, NULL));

  /* Record my_ref in case it bounces */

  data_open_reference = dop.hdr.my_ref;

  return NULL;
}

/*************************************************/
/* protocols_atats_data_open_bounced()           */
/*                                               */
/* For a View Source function, the browser       */
/* broadcasts a Message_DataOpen. If this        */
/* bounces, attempt to start an editor instead,  */
/* with the pathname specified in the message.   */
/*                                               */
/* See PRM 3-265.                                */
/*                                               */
/* Parameters: Pointer to the WimpMessage struct */
/*             for the received message.         */
/*************************************************/

_kernel_oserror * protocols_atats_data_open_bounced(WimpMessage * m)
{
  #ifdef TRACE
    if (tl & (1u<<28)) Printf("protocols_atats_data_open_bounced: Called\n");
  #endif

  if (m->hdr.my_ref == data_open_reference)
  {
    _kernel_oserror * e;
    char            * combuf;
    char            * comstart = "If \"Alias$@RunType_FFF\" <> \"\" Then @RunType_FFF ";
    int               len;

    data_open_reference = 0;

    /* Want to run '@RunType_FFF' with the given pathname as a parameter;  */
    /* need to assemble this command string. Work out how long it will be. */

    len = strlen(comstart) + strlen(m->data.data_open.path_name) + 1;

    /* Allocate space for it (complain if this fails). */

    combuf = malloc(len);

    if (!combuf) return make_no_memory_error(9);

    /* Assemble the string */

    strcpy(combuf, comstart);
    strcat(combuf, m->data.data_open.path_name);

    /* Execute the command */

    e = _swix(Wimp_StartTask,
              _IN(0),

              combuf);

    /* Free the command buffer and return the value the _swix call gave back */

    free(combuf);
    combuf = NULL;

    return e;
  }

  #ifdef TRACE

    else
    {
      erb.errnum = Utils_Error_Custom_Normal;

      sprintf(erb.errmess,
              "Message reference %x not recognised in protocols_atats_data_open_bounced",
              m->hdr.your_ref);

      return &erb;
    }

  #endif

  return NULL;
}

/*************************************************/
/* protocols_atats_send_data_save()              */
/*                                               */
/* Send out a Message_DataSave to initiate       */
/* saving of data to another applicaion.         */
/*                                               */
/* See PRM 3-250 to 3-252.                       */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             relevant to the save (or NULL);   */
/*                                               */
/*             Pointer to an item that will vary */
/*             depending on what is being saved  */
/*             - e.g. an HStream struct, a       */
/*             hotlist item, or NULL... full     */
/*             details by the definition of the  */
/*             protocols_saving enumeration in   */
/*             Protocols.h;                      */
/*                                               */
/*             Pointer to a null terminated      */
/*             leafname to use;                  */
/*                                               */
/*             If possible, the estimated size   */
/*             of the data, else -1;             */
/*                                               */
/*             Filetype to quote;                */
/*                                               */
/*             A protocols_saving value to say   */
/*             what is being saved (see          */
/*             Protocols.h);                     */
/*                                               */
/*             A WimpGetPointerInfo block        */
/*             pointer, from which the window    */
/*             and icon handle to send to, and   */
/*             x and y coords to send to, are    */
/*             read.                             */
/*************************************************/

_kernel_oserror * protocols_atats_send_data_save(browser_data * b, void * extra, char * leaf, int estimated_size, int filetype,
                                                 protocols_saving saving, WimpGetPointerInfoBlock * info)
{
  WimpMessage m;

  #ifdef TRACE
    if (tl & (1u<<28)) Printf("protocols_atats_send_data_save: Called\n");
  #endif

  /* Fill in the header */

  m.hdr.sender      = task_handle;
  m.hdr.your_ref    = 0;
  m.hdr.action_code = Wimp_MDataSave;

  /* Fill in the message body */

  m.data.data_save.destination_window = info->window_handle;
  m.data.data_save.destination_icon   = info->icon_handle;
  m.data.data_save.destination_x      = info->x;
  m.data.data_save.destination_y      = info->y;
  m.data.data_save.estimated_size     = estimated_size;
  m.data.data_save.file_type          = filetype;

  /* Copy in the leafname and work out the message size */

  StrNCpy0(m.data.data_save.leaf_name, leaf);

  m.hdr.size = (int) WordAlign(strlen(m.data.data_save.leaf_name) + 45);

  /* Send it */

  RetError(wimp_send_message(Wimp_EUserMessage,
                             &m,
                             info->window_handle,
                             info->icon_handle,
                             NULL));

  /* Remember various details */

  switch (saving)
  {
    /* Save the document source */

    case protocols_saving_document_source:
    {
      save_source_reference              = m.hdr.my_ref;
      save_source_transmitted            = 0;
      save_source_browser                = b;
    }
    break;

    /* Export the document as text */

    case protocols_saving_document_as_text:
    {
      save_as_text_reference             = m.hdr.my_ref;
      save_as_text_browser               = b;
    }
    break;

    /* Export the document as text */

    case protocols_saving_document_as_draw:
    {
      save_as_draw_reference             = m.hdr.my_ref;
      save_as_draw_browser               = b;
      save_as_draw_backgrounds           = savefile_alternative_selected();
    }
    break;

    /* Save a link as a URI file */

    case protocols_saving_link:
    {
      save_link_reference                = m.hdr.my_ref;
      save_link_transmitted              = 0;
      save_link_browser                  = b;
      save_link_token                    = (HStream *) extra;
      save_link_as_url                   = savefile_alternative_selected();
    }
    break;

    /* Save the current location as a URI file */

    case protocols_saving_frame_location:
    {
      save_location_reference            = m.hdr.my_ref;
      save_location_transmitted          = 0;
      save_location_browser              = b;
      save_location_as_url               = savefile_alternative_selected();
    }
    break;

    #ifndef REMOTE_HOTLIST

      /* Save a hotlist item as a URI file */

      case protocols_saving_hotlist_entry:
      {
        save_hotlist_entry_reference       = m.hdr.my_ref;
        save_hotlist_entry_transmitted     = 0;
        save_hotlist_entry_item            = (hotlist_item *) extra;
        save_hotlist_entry_as_url          = filetype == FileType_URI ? 0 : 1;
      }
      break;

      /* Save a selection of hotlist items as an HTML file */

      case protocols_saving_hotlist_selection:
      {
        save_hotlist_selection_reference   = m.hdr.my_ref;
        save_hotlist_selection_transmitted = 0;
      }
      break;

      /* Save the entire hotlist as an HTML file */

      case protocols_saving_entire_hotlist:
      {
        save_entire_hotlist_reference      = m.hdr.my_ref;
        save_entire_hotlist_transmitted    = 0;
      }
      break;

    #endif

    /* Save an object through the fetcher */

    case protocols_saving_object:
    {
      save_object_reference              = m.hdr.my_ref;
      save_object_transmitted            = 0;
      save_object_browser                = b;
      save_object_through_scrap          = 0;
    }
    break;

    /* Save an image as a sprite */

    case protocols_saving_image_sprite:
    {
      save_image_reference               = m.hdr.my_ref;
      save_image_transmitted             = 0;
      save_image_browser                 = b;
      save_image_token                   = (HStream *) extra;
      save_image_as_original             = savefile_alternative_selected();
    }
    break;

    /* Saving the local or global history */

    case protocols_saving_local_history:
    {
      save_history_reference             = m.hdr.my_ref;
      save_history_browser               = b;
    }
    break;

    case protocols_saving_global_history:
    {
      save_history_reference             = m.hdr.my_ref;
      save_history_browser               = NULL;
    }
    break;
  }

  /* Finished */

  return NULL;
}

/*************************************************/
/* protocols_atats_data_save_bounced()           */
/*                                               */
/* Deals with a Message_DataSave bouncing.       */
/*                                               */
/* See PRM 3-252.                                */
/*                                               */
/* Parameters: Pointer to the WimpMessage struct */
/*             for the received message.         */
/*************************************************/

_kernel_oserror * protocols_atats_data_save_bounced(WimpMessage * m)
{
  #ifdef TRACE
    if (tl & (1u<<28)) Printf("protocols_atats_data_save_bounced: Called\n");
  #endif

  /* We have nothing to do here at present; this */
  /* is only here to be complete.                */

  return NULL;
}

/*************************************************/
/* protocols_atats_got_ram_fetch()               */
/*                                               */
/* After the browser sends a Message_DataSave to */
/* an application, it may reply with             */
/* Message_RAMFetch rather than                  */
/* Message_DataSaveAck if it can handle RAM      */
/* transfer. In that case, we should see if we   */
/* can do RAM transfer for this filetype         */
/* ourselves, and if so, transfer data.          */
/*                                               */
/* See PRM 3-255 and 256.                        */
/*                                               */
/* Parameters: Pointer to the WimpMessage struct */
/*             for the Message_RAMFetch. The     */
/*             contents will be modified and     */
/*             used in a reply.                  */
/*************************************************/

_kernel_oserror * protocols_atats_got_ram_fetch(WimpMessage * m)
{
  #ifdef TRACE
    if (tl & (1u<<28)) Printf("protocols_atats_got_ram_fetch: Called\n");
  #endif

  /* Only the transfer methods by which RAM saving is currently */
  /* supported are listed in the switch. For others, we just do */
  /* nothing. The other application will see its message bounce */
  /* and should send Message_DataSaveAck to continue the save   */
  /* through a temporary file on disc. If not, no loss - from   */
  /* the user's perspective, nothing will have happened.        */
  /*                                                            */
  /* Note all the functions called in the switch statement call */
  /* Protocols.c back again with requests to send a reply of    */
  /* Message_RAMTransmit if they're successful.                 */
  /*                                                            */
  /* Stop press. C can't cope - you can't have a non-constant   */
  /* in a case statement. Good grief. So, this nice elegant     */
  /* case statement gets degraded into a grotty if...else       */
  /* ladder. Yuk, yuk, yuk, yuk, yuk. I *hate* this language.   */

  if (m->hdr.your_ref && m->hdr.your_ref == save_source_reference)
  {
    /* Save document source */

    if (
         is_known_browser(save_source_browser)
       )
       return save_transfer_source(save_source_browser,
                                   &save_source_transmitted,
                                   m);
  }
  else if (m->hdr.your_ref && m->hdr.your_ref == save_link_reference)
  {
    if (is_known_browser(save_link_browser))
    {
      if (save_link_token)
      {
        /* Save a link as a URI file */

        return save_transfer_uri(save_link_token->anchor,
                                 NULL,
                                 save_link_as_url,
                                 &save_link_transmitted,
                                 m);
      }
      else
      {
        /* Save the current location as a URI file */

        char * url   = browser_current_url  (save_link_browser);
        char * title = browser_current_title(save_link_browser);

        if (!url) url = " ";

        return save_transfer_uri(url,
                                 title,
                                 save_link_as_url,
                                 &save_link_transmitted,
                                 m);
      }
    }
  }
  else if (m->hdr.your_ref && m->hdr.your_ref == save_location_reference)
  {
    /* Save the current location as a URI file */

    if (is_known_browser(save_location_browser))
    {
      char * title = browser_current_title(save_location_browser);

      return save_transfer_uri(browser_current_url(save_location_browser),
                               title,
                               save_location_as_url,
                               &save_location_transmitted,
                               m);
    }
  }

  #ifndef REMOTE_HOTLIST

    else if (m->hdr.your_ref && m->hdr.your_ref == save_hotlist_entry_reference)
    {
      /* Save a hotlist item as a URI file */

      if (
           save_hotlist_entry_item
         )
         return save_transfer_uri(save_hotlist_entry_item->data.url,
                                  save_hotlist_entry_item->name,
                                  save_hotlist_entry_as_url,
                                  &save_hotlist_entry_transmitted,
                                  m);
    }

  #endif

  return NULL;
}

/*************************************************/
/* protocols_atats_send_ram_transmit()           */
/*                                               */
/* Send out a Message_RAMTransmit in response to */
/* a Message_RAMFetch from another application,  */
/* as part of an ongoing dialogue for RAM        */
/* transfer.                                     */
/*                                               */
/* See PRM 3-255 and 256.                        */
/*                                               */
/* Parameters: Pointer to the WimpMessage struct */
/*             for the Message_RAMFetch. The     */
/*             contents will be modified and     */
/*             used in a reply;                  */
/*                                               */
/*             The number of bytes written to    */
/*             the buffer given in the           */
/*             Message_RAMFetch;                 */
/*                                               */
/*             1 if this is the last message in  */
/*             the transfer (so it won't be sent */
/*             UserMessageRecorded and raise an  */
/*             error when it doesn't get         */
/*             acknowledged...).                 */
/*************************************************/

_kernel_oserror * protocols_atats_send_ram_transmit(WimpMessage * m, int transmitted, int last)
{
  int old_ref = m->hdr.your_ref;

  #ifdef TRACE
    if (tl & (1u<<28)) Printf("protocols_atats_send_ram_transmit: Called\n");
  #endif

  /* Fill in the parts that need changing */

  m->hdr.your_ref             = m->hdr.my_ref;
  m->hdr.action_code          = Wimp_MRAMTransmit;

  m->data.ram_transmit.nbytes = transmitted;

  #ifdef TRACE

    /* Show the contents of the transmission buffer */

    if (tl & (1u<<28))
    {
      void * local = malloc(m->data.ram_transmit.nbytes);

      if (local)
      {
        if (
             !wimp_transfer_block(m->hdr.sender,
                                  m->data.ram_transmit.buffer,
                                  task_handle,
                                  local,
                                  m->data.ram_transmit.nbytes)
           )
        {
          trace_dump_buffer(m->data.ram_transmit.buffer,
                            m->data.ram_transmit.nbytes,
                            1);
        }
        else Printf("\n(Cannot dump transmission buffer - error from Wimp_TransferBlock)\n\n");
      }
      else
      {
        Printf("\n(Cannot dump transmission buffer - not enough memory for local copy)\n\n");
      }
    }

  #endif

  /* Send the reply */

  RetError(wimp_send_message(last ? Wimp_EUserMessage : Wimp_EUserMessageRecorded,
                             m,
                             m->hdr.sender,
                             0,
                             NULL));

  /* Update the records of my_ref for subsequent replies and bounces */

  protocols_util_update_reference(old_ref, m->hdr.my_ref);

  /* If this is the last item, may need to do some tidying up */
  /* (don't do this before sending the message, as here we    */
  /* close the menu tree - if the window we were sending to   */
  /* was in a menu... Well, things Go Wrong.                  */

  if (last)
  {
    #ifndef REMOTE_HOTLIST
      if (m->hdr.my_ref == save_hotlist_entry_reference) RetError(hotlist_clear_selection());
    #endif

    if (
         m->hdr.my_ref == save_source_reference        ||
         m->hdr.my_ref == save_link_reference          ||
         m->hdr.my_ref == save_location_reference

         #ifndef REMOTE_HOTLIST
           || m->hdr.my_ref == save_hotlist_entry_reference
         #endif
       )
    {
      _swix(Wimp_CreateMenu,
            _IN(1),

            -1);
    }
  }

  /* Finished */

  return NULL;
}

/*************************************************/
/* protocols_atats_ram_transmit_bounced()        */
/*                                               */
/* If a RAMTransmit is not acknowledged, we      */
/* should abort file transfer and raise an       */
/* error.                                        */
/*                                               */
/* See PRM 3-255 and 256.                        */
/*                                               */
/* Parameters Pointer to the WimpMessage struct  */
/*            for the received message.          */
/*************************************************/

_kernel_oserror * protocols_atats_ram_transmit_bounced(WimpMessage * m)
{
  #ifdef TRACE
    if (tl & (1u<<28)) Printf("protocols_atats_ram_transmit_bounced: Called\n");
  #endif

  /* There's no tidying up to do; just raise an error. */

  erb.errnum = Utils_Error_Custom_Message;

  StrNCpy0(erb.errmess,
           lookup_token("RecvDied:Data transfer failed - receiver died.",
                        0,
                        0));

  return &erb;
}

/*************************************************/
/* protocols_atats_got_data_save_ack()           */
/*                                               */
/* Once we've sent out Message_DataSave we could */
/* get Message_RAMFetch back and proceed with    */
/* RAM transfer. Or the other task could respond */
/* with a Message_DataSaveAck, for transfer via  */
/* a temporary file on disc. This can also       */
/* happen if the other application responds with */
/* a Message_RAMFetch but we decide we can't do  */
/* RAM transfer for that particular object;      */
/* the application sees its Message_RAMFetch     */
/* bounce and drops back to Message_DataSaveAck  */
/* instead.                                      */
/*                                               */
/* See PRM 3-250, 251 and 253.                   */
/*                                               */
/* Parameters: Pointer to the WimpMessage struct */
/*             for the received message. The     */
/*             contents will be modified and     */
/*             used in a reply.                  */
/*************************************************/

static _kernel_oserror * protocols_atats_got_data_save_ack(WimpMessage * m)
{
  _kernel_oserror * e    = NULL;
  int               ok   = 0; /* 0 = problem, 1 = OK, send DataLoad, 2 = OK, but don't send DataLoad */
  char            * path = m->data.data_save_ack.leaf_name; /* (Daft structure definition - it's a path, not a leaf...) */

  #ifdef TRACE
    if (tl & (1u<<28)) Printf("protocols_atats_got_data_save_ack: Called\n");
  #endif

  /* First, save the file. Yes, it's another nasty if...else ladder */
  /* because C can't handle it as a switch statement...             */

  if (m->hdr.your_ref && m->hdr.your_ref == save_source_reference)
  {
    /* Save the document source */

    if (is_known_browser(save_source_browser))
    {
      e = save_save_source(path, save_source_browser);
      if (!e) ok = 1;
    }
  }
  else if (m->hdr.your_ref && m->hdr.your_ref == save_as_text_reference)
  {
    /* Export the document as text */

    if (is_known_browser(save_as_text_browser))
    {
      e = savetext_save_text(save_as_text_browser, path);
      if (!e) ok = 1;
    }
  }
  else if (m->hdr.your_ref && m->hdr.your_ref == save_as_draw_reference)
  {
    /* Export the document as Draw */

    if (is_known_browser(save_as_draw_browser))
    {
      e = savedraw_save_draw(save_as_draw_browser, path, save_as_draw_backgrounds);
      if (!e) ok = 1;
    }
  }
  else if (m->hdr.your_ref && m->hdr.your_ref == save_link_reference)
  {
    if (is_known_browser(save_link_browser))
    {
      if (save_link_token)
      {
        /* Save a link as a URI file */

        e = save_save_uri(path, save_link_token->anchor, NULL, save_link_as_url);
        if (!e) ok = 1;
      }
      else
      {
        char * url   = browser_current_url  (save_link_browser);
        char * title = browser_current_title(save_link_browser);

        /* Save the current location as a URI file */

        if (!url) url = " ";

        e = save_save_uri(path, url, title, 0);
        if (!e) ok = 1;
      }
    }
  }
  else if (m->hdr.your_ref && m->hdr.your_ref == save_location_reference)
  {
    /* Save the current location as a URI file */

    if (is_known_browser(save_location_browser))
    {
      char * title = browser_current_title(save_location_browser);

      e = save_save_uri(path, browser_current_url(save_location_browser), title, save_location_as_url);
      if (!e) ok = 1;
    }
  }

  #ifndef REMOTE_HOTLIST

    else if (m->hdr.your_ref && m->hdr.your_ref == save_hotlist_entry_reference)
    {
      /* Save a hotlist item as a URI file */

      if (save_hotlist_entry_item)
      {
        e = save_save_uri(path, save_hotlist_entry_item->data.url, save_hotlist_entry_item->name, save_hotlist_entry_as_url);

        if (!e)
        {
          ok = 1;
          e = hotlist_clear_selection();
        }
      }
    }
    else if (m->hdr.your_ref && m->hdr.your_ref == save_hotlist_selection_reference)
    {
      /* Save a hotlist selection as an HTML file */

      e = hotlist_save_hotlist(path, NULL, 1);

      if (!e)
      {
        ok = 1;
        e = hotlist_clear_selection();
      }
    }
    else if (m->hdr.your_ref && m->hdr.your_ref == save_entire_hotlist_reference)
    {
      /* Save the whole hotlist */

      e = hotlist_save_hotlist(path, NULL, 0);
      if (!e) ok = 1;
    }

  #endif

  else if (m->hdr.your_ref && m->hdr.your_ref == save_history_reference)
  {
    /* Save the local or global history */

    if (is_known_browser(save_history_browser))
    {
      e = history_save_as_html(path, save_history_browser);
      if (!e) ok = 1;
    }
    else
    {
      e = history_save_as_html(path, NULL);
      if (!e) ok = 1;
    }
  }
  else if (m->hdr.your_ref && m->hdr.your_ref == save_object_reference)
  {
    if (is_known_browser(save_object_browser))
    {
      /* First, hide the Save Object dialogue */

      e = saveobject_close(save_object_browser);

      if (!e)
      {
        /* If this is through scrap, use a different filename */

        if (m->data.data_save_ack.estimated_size == -1)
        {
          /* Need to store the Message_DataLoad for later */

          save_object_browser->pending_data_load = malloc(sizeof(WimpMessage));
          if (!save_object_browser->pending_data_load) return make_no_memory_error(13);

          #ifdef TRACE
            malloccount += sizeof(WimpMessage);
            if (tl & (1u<<13)) Printf("** malloccount (protocols_atats_got_data_save_ack): \0211%d\0217\n",malloccount);
          #endif

          *save_object_browser->pending_data_load = *m;

          /* Change the filename to something unique and */
          /* update the message length                   */

          protocols_util_make_unique_name(save_object_browser->pending_data_load->data.data_load.leaf_name,
                                          sizeof(save_object_browser->pending_data_load->data.data_load.leaf_name));

          save_object_browser->pending_data_load->hdr.size = (int) WordAlign(strlen(save_object_browser->pending_data_load->data.data_load.leaf_name) + 45);

          /* Exit through the save routine */

          return save_save_object(save_object_browser->pending_data_load->data.data_load.leaf_name,
                                  save_object_browser);
        }

        /* Otherwise, save as normal */

        else
        {
          e = save_save_object(path, save_object_browser);
          if (!e) ok = 1;
        }
      }
    }
  }
  else if (m->hdr.your_ref && m->hdr.your_ref == save_image_reference)
  {
    if (is_known_browser(save_image_browser))
    {
      if (!save_image_as_original)
      {
        /* Save as a sprite */

        e = image_export_sprite(path, save_image_browser, save_image_token);
        if (!e) ok = 1;
      }
      else
      {
        /* Save original image - similar to Save Object code. So, */
        /* if this is through scrap, use a different filename.    */

        if (m->data.data_save_ack.estimated_size == -1)
        {
          WimpMessage * pending;

          /* Need to store the Message_DataLoad for later */

          pending = malloc(sizeof(WimpMessage));
          if (!pending) return make_no_memory_error(13);

          #ifdef TRACE
            malloccount += sizeof(WimpMessage);
            if (tl & (1u<<13)) Printf("** malloccount (protocols_atats_got_data_save_ack): \0211%d\0217\n",malloccount);
          #endif

          *pending = *m;

          /* Change the filename to something unique and */
          /* update the message length                   */

          protocols_util_make_unique_name(pending->data.data_load.leaf_name,
                                          sizeof(pending->data.data_load.leaf_name));

          pending->hdr.size = (int) WordAlign(strlen(pending->data.data_load.leaf_name) + 45);

          /* Exit through the save routine */

          e = image_export_original(pending->data.data_load.leaf_name,
                                    save_image_browser,
                                    save_image_token);

          if (!e)
          {
            save_image_browser                    = last_browser;
            save_image_browser->pending_data_load = pending;
            ok                                    = 2;
          }
        }

        /* Otherwise, save as normal */

        else
        {
          e = image_export_original(path,
                                    save_image_browser,
                                    save_image_token);

          if (!e)
          {
            save_image_browser = last_browser;
            ok                 = 2;
          }
        }
      }
    }
  }

  /* If everything is OK, send out a Message_DataLoad in reply */

  if (ok && !e)
  {
    if (ok == 1) e = protocols_atats_send_data_load(m);

    /* Don't close the menu before sending the message, as if the item we are */
    /* sending to is within a menu tree, an Illegal Window Handle error may   */
    /* be raised.                                                             */

    _swix(Wimp_CreateMenu,
          _IN(1),

          -1);
  }

  /* Finished */

  return e;
}

/*************************************************/
/* protocols_atats_send_data_load()              */
/*                                               */
/* Send out a Message_DataLoad in response to a  */
/* Message_DataSaveAck from another application, */
/* as part of an ongoing dialogue for file       */
/* transfer.                                     */
/*                                               */
/* See PRM 3-251 and 253.                        */
/*                                               */
/* Parameters: Pointer to the WimpMessage struct */
/*             for the Message_DataSaveAck. The  */
/*             contents will be modified and     */
/*             used in the reply.                */
/*************************************************/

_kernel_oserror * protocols_atats_send_data_load(WimpMessage * m)
{
  int old_ref = m->hdr.your_ref;
  int found   = 0;

  #ifdef TRACE
    if (tl & (1u<<28)) Printf("protocols_atats_send_data_load: Called\n");
  #endif

  /* Fill in the parts that need changing */

  m->hdr.your_ref    = m->hdr.my_ref;
  m->hdr.action_code = Wimp_MDataLoad;

  /* Find out the file size and type */

  RetError(_swix(OS_File,
                 _INR(0,1) | _OUT(0) | _OUT(4) | _OUT(6),

                 23, /* Read catalogue info for named, stamped object */
                 m->data.data_save_ack.leaf_name,

                 &found,
                 &m->data.data_load.estimated_size,
                 &m->data.data_load.file_type));

  if (found != 1)
  {
    /* Object not found - woo, weird (but it happens!) */

    #ifdef TRACE
      {
        erb.errnum = Utils_Error_Custom_Message;

        StrNCpy0(erb.errmess,
                 "Can't find the file I'm supposed to tell someone else to load in protocols_atats_send_data_load");

        show_error_ret(&erb);
      }
    #endif

    return NULL;
  }

  /* Send the reply */

  RetError(wimp_send_message(Wimp_EUserMessageRecorded,
                             m,
                             m->data.data_save_ack.destination_window,
                             m->data.data_save_ack.destination_icon,
                             NULL));

  /* Update the records of my_ref for subsequent replies and bounces */

  protocols_util_update_reference(old_ref, m->hdr.my_ref);

  /* Finished */

  return NULL;
}

/*************************************************/
/* protocols_atats_data_load_bounced()           */
/*                                               */
/* Called if a Message_DataLoad bounces. Need to */
/* delete any scrap files written and report an  */
/* appropriate error.                            */
/*                                               */
/* See PRM 3-253, 254.                           */
/*                                               */
/* Parameters Pointer to the WimpMessage struct  */
/*            for the received message.          */
/*************************************************/

_kernel_oserror * protocols_atats_data_load_bounced(WimpMessage * m)
{
  int scrap_was_opened = 0;

  #ifdef TRACE
    if (tl & (1u<<28)) Printf("protocols_atats_data_load_bounced: Called\n");
  #endif

  /* Do we recognise this message? If so, deal with it, otherwise */
  /* ignore it (or raise a warning in TRACE builds).              */

  if (
       m->hdr.my_ref == save_source_reference              ||
       m->hdr.my_ref == save_as_text_reference             ||
       m->hdr.my_ref == save_as_draw_reference             ||
       m->hdr.my_ref == save_link_reference                ||
       m->hdr.my_ref == save_location_reference            ||

       #ifndef REMOTE_HOTLIST

         m->hdr.my_ref == save_hotlist_entry_reference     ||
         m->hdr.my_ref == save_hotlist_selection_reference ||
         m->hdr.my_ref == save_entire_hotlist_reference    ||

       #endif

       m->hdr.my_ref == save_object_reference              ||
       m->hdr.my_ref == save_image_reference               ||
       m->hdr.my_ref == save_history_reference
     )
  {
    /* Not interested in any errors here - just get rid of scrap if we can */

    if (
         !strcmp(m->data.data_load.leaf_name,
                 Save_ScrapFile)
       )
    {
      /* Special case - if saving an object (Shift+Click on a link */
      /* or whatever) through scrap, so the send of a pending      */
      /* Message_DataLoad led to this bounce, don't delete the     */
      /* scrap file as it's the only copy of the data that we      */
      /* have. Instead, open the scrap directory. Otherwise, do as */
      /* PRM 3-254 and delete the scrap file.                      */

      if (!save_object_through_scrap) remove (m->data.data_load.leaf_name);
      else
      {
        char * combuf;
        int    comlen;
        char   new_name[Limits_OS_Pathname];

        save_object_through_scrap = 0;

        /* The first thing we need to do is change the filename away */
        /* from Scrap, or it could get overwritten when someone else */
        /* does a transfer.                                          */

        protocols_util_make_unique_name(new_name, sizeof(new_name));

        comlen = strlen("Rename ")                   +
                 strlen(m->data.data_load.leaf_name) +
                 1                                   + /* Space between the two names */
                 strlen(new_name)                    +
                 1;                                    /* Terminating byte */

        /* Allocate space for the Rename command; if it fails, fine */
        /* - just don't rename.                                     */

        combuf = malloc(comlen);

        if (combuf)
        {
          sprintf(combuf,
                  "%s%s%c%s",
                  "Rename ",
                  m->data.data_load.leaf_name,
                  ' ',
                  new_name);

          /* Execute the command */

          _swix(Wimp_StartTask,
                _IN(0),
                combuf);

          /* Free the buffer */

          free(combuf);
          combuf = NULL;
        }

        /* Try and allocate a buffer for the Filer_OpenDir command. */
        /* If this fails, let it do so silently.                    */

        comlen = strlen("Filer_OpenDir ") + strlen(Save_ScrapDir) + 1;
        combuf = malloc(comlen);

        if (combuf)
        {
          /* Copy in the command */

          strcpy(combuf, "Filer_OpenDir ");
          strcat(combuf, Save_ScrapDir);

          /* Execute it - if there was no error, flag that Scrap was opened */

          if (
               !_swix(Wimp_StartTask,
                      _IN(0),

                      combuf)
             )
             scrap_was_opened = 1;

          /* Free the temporary buffer */

          free(combuf);
        }
      }
    }

    /* Report an appropriate message */

    erb.errnum = Utils_Error_Custom_Message;


    if (!scrap_was_opened)
    {
      StrNCpy0(erb.errmess,
               lookup_token("RecvDied:Data transfer failed - receiver died.",
                            0,
                            0));
    }
    else
    {
      StrNCpy0(erb.errmess,
               lookup_token("RecvDiedKept:Data transfer failed - receiver died. The temporary file has been kept on disc so the fetched data can be recovered.",
                            0,
                            0));
    }

    return &erb;
  }

  #ifdef TRACE

    /* In TRACE builds, raise a warning if we don't recognise the message */

    else
    {
      erb.errnum = Utils_Error_Custom_Normal;

      sprintf(erb.errmess,
              "Message reference %x not recognised in protocols_atats_data_load_bounced",
              m->hdr.your_ref);

      return &erb;
    }

  #endif

  return NULL;
}

/*************************************************/
/* protocols_atats_got_data_load_ack()           */
/*                                               */
/* Deals with a Message_DataLoadAck - completion */
/* of app to app transfer from the browser.      */
/*                                               */
/* See PRM 3-250, 251, 254.                      */
/*                                               */
/* Parameters: Pointer to the WimpMessage struct */
/*             for the received message.         */
/*************************************************/

_kernel_oserror * protocols_atats_got_data_load_ack(WimpMessage * m)
{
  #ifdef TRACE
    if (tl & (1u<<28)) Printf("protocols_atats_got_data_load_ack: Called\n");
  #endif

  /* We have nothing to do here at present; this */
  /* is only here to be complete.                */

  return NULL;
}

/*************************************************/
/* protocols_atats_send_any_pendings()           */
/*                                               */
/* Sends any pending messages related to the     */
/* given browser, in the context of application  */
/* to application saving.                        */
/*                                               */
/* Parameters: Pointer to a browser_data struct  */
/*             relevant to the pending messages, */
/*             if any.                           */
/*************************************************/

_kernel_oserror * protocols_atats_send_any_pendings(browser_data * b)
{
  _kernel_oserror * e = NULL;

  if (!b || !is_known_browser(b)) return NULL;

  /* Pending Message_DataLoad - an object has been saved */
  /* through the fetcher to a temporary file.            */

  if (b->pending_data_load)
  {
    char * combuf;
    int    comlen;
    int    type;

    b->pending_data_load->hdr.my_ref = 0;

    /* Update the other state variables */

    save_object_reference     = b->pending_data_load->hdr.your_ref;
    save_object_transmitted   = 0;
    save_object_browser       = b;
    save_object_through_scrap = 1;

    /* Delete scrap if present, rename the file we've written to */
    /* ScrapFile, and alter the message back to using this       */
    /* filename.                                                 */

    remove(Save_ScrapFile);

    /* Does Scrap still exist? The above may have failed because */
    /* Save_ScrapFile was in use elsewhere. If so, then leave    */
    /* the filename as-is.                                       */

    type = 1;

    _swix(OS_File,
          _INR(0,1) | _OUT(0),

          17, /* Read catalogue info for object */
          Save_ScrapFile,

          &type);

    if (!type)
    {
      /* Allocate space for the buffer */

      comlen = strlen("Rename ")                                      +
               strlen(b->pending_data_load->data.data_load.leaf_name) +
               1                                                      + /* The space between the two names */
               strlen(Save_ScrapFile)                                 +
               1;                                                       /* Terminating byte */

      combuf = malloc(comlen);

      /* If we can't allocate space, as with the case of the scrap */
      /* file being undeletable, stick with the existing filename. */

      if (combuf)
      {
        /* We can allocate space, so build the command string. */

        sprintf(combuf,
                "%s%s%c%s",
                "Rename ",
                b->pending_data_load->data.data_load.leaf_name,
                ' ',
                Save_ScrapFile);

        /* Do it. If the command fails, use the original filename. */

        if (
             !_swix(Wimp_StartTask,
                    _IN(0),

                    combuf)
           )
        {
          /* The command worked, so change the message appropriately. */

          StrNCpy0(b->pending_data_load->data.data_load.leaf_name, Save_ScrapFile);

          b->pending_data_load->hdr.size = (int) WordAlign(strlen(Save_ScrapFile) + 45);
        }

        /* Free the command buffer */

        free(combuf);
      }
    }

    /* At last, send the message */

    e = protocols_atats_send_data_load(b->pending_data_load);

    /* We don't need the message block in the browser_data structure */
    /* any more, so free it.                                         */

    free(b->pending_data_load);
    b->pending_data_load = NULL;
  }

  return e;
}

/*************************************************/
/* protocols_atatl_got_data_open()               */
/*                                               */
/* Handle reception of a Message_DataOpen - we   */
/* may want to load a given file. It is an       */
/* application to application related the        */
/* consideration as we are transfering data      */
/* from the Filer, in a sense.                   */
/*                                               */
/* See PRM 3-265.                                */
/*                                               */
/* Parameters: Pointer to the WimpMessage struct */
/*             for the received message. The     */
/*             contents will be modified and     */
/*             used in a reply.                  */
/*************************************************/

_kernel_oserror * protocols_atatl_got_data_open(WimpMessage * m)
{
  #ifdef TRACE
    if (tl & (1u<<28)) Printf("protocols_atatl_got_data_open: Called\n");
  #endif

  /* Don't load it if we're the originator */

  if (m->hdr.sender == task_handle) return NULL;

  /* Don't want to load a text or data file from double-clicking, */
  /* only by dragging to a window or the icon bar icon. Similarly */
  /* ANT's URL files should only be loaded if dragged on, and the */
  /* same is true of URI files if we are using the URI handler.   */
  /* Since there is no native TIFF support, drop that out too.    */

  if (
       m->data.data_open.file_type == FileType_TEXT ||
       m->data.data_open.file_type == FileType_DATA ||
       m->data.data_open.file_type == FileType_DOS  || /* (Equivalent to FileType_DATA for PC files, so to speak) */
       m->data.data_open.file_type == FileType_TIFF ||
       m->data.data_open.file_type == FileType_URL  ||
       (
         m->data.data_open.file_type == FileType_URI &&
         uri_module_present
       )
     )
     return NULL;

  /* Now treat as a DataLoad message to avoid duplicating load code. */

  m->data.data_load.destination_window = 0; /* Force a new window to open */
  m->data.data_load.destination_icon   = -1;
  m->data.data_load.estimated_size     = 0;

  #ifdef TRACE
    if (tl & (1u<<28)) Printf("protocols_atatl_got_data_open: Exitting through protocols_atatl_got_data_load\n");
  #endif

  return protocols_atatl_got_data_load(m);
}

/*************************************************/
/* protocols_atatl_got_data_load()               */
/*                                               */
/* Deals with a Message_DataLoad - if we can     */
/* handle the file, load it.                     */
/*                                               */
/* See PRM 3-253.                                */
/*                                               */
/* Parameters: Pointer to the WimpMessage struct */
/*             for the received message. The     */
/*             contents will be modified and     */
/*             used in a reply.                  */
/*************************************************/

_kernel_oserror * protocols_atatl_got_data_load(WimpMessage * m)
{
  int         filetype = m->data.data_load.file_type;
  ComponentId c;

  #ifdef TRACE
    if (tl & (1u<<28)) Printf("protocols_atatl_got_data_load: Called\n");
  #endif

  /* Was the URL writable targetted? In this case, if it was a text file, */
  /* assume it contained a URL - modify the Wimp Message block to contain */
  /* an ANT URL file filetype.                                            */

  RetError(window_wimp_to_toolbox(0,
                                  m->data.data_save.destination_window,
                                  m->data.data_save.destination_icon,
                                  NULL,
                                  &c));

  if (c == URLBarWrit && filetype == FileType_TEXT) filetype = FileType_URL;

  /* If we've got a Data or DOS filetype, can we get anything */
  /* more from the filename?                                  */

  if (
       filetype == FileType_DATA ||
       filetype == FileType_DOS
     )
  {
    char * ext = strrchr(m->data.data_load.leaf_name, '/'); /* Since under RISC OS, filename extensions will start with '/' */

    if (ext)
    {
      ext++;

      if (mimemap_extension_to_riscos(ext, &filetype)) filetype = FileType_DATA;
    }
  }

  /* Proceed only if it's a filetype we can handle */

  if (
       filetype == FileType_HTML ||
       filetype == FileType_TEXT ||
       filetype == FileType_GIF  ||
       filetype == FileType_JPEG ||
       filetype == FileType_PNG  ||
       filetype == FileType_TIFF ||
       filetype == FileType_URL  ||
       filetype == FileType_URI
     )
  {
    char url  [Limits_URL];
    char title[Limits_Title];

    int  apptoapp = m->hdr.your_ref ? 1 : 0; /* your_ref is zero if from filer, else filled in for app to app */

    #ifdef TRACE
      if (tl & (1u<<28)) Printf("protocols_atatl_got_data_load: Can handle this filetype (0x%03x)\n", filetype);
    #endif

    /* Multiuser builds, if not logged in, just acknowledge */
    /* the message to stop (e.g.) another browser being     */
    /* launched by the Filer, then complain.                */

    #ifndef SINGLE_USER

      #ifdef TRACE
        if (tl & (1u<<28)) Printf("protocols_atatl_got_data_load: Not logged in\n");
      #endif

      if (!logged_in)
      {
        show_error_ret(protocols_atatl_send_data_load_ack(m));

        erb.errnum = Utils_Error_Custom_Message;

        StrNCpy0(erb.errmess,
                 lookup_token("MustLogIn:The browser cannot fetch Web pages until you log in.",
                              0,
                              0));

        show_error_ret(&erb);

        return NULL;
      }

    #endif

    /* Process the file */

    if (
         filetype != FileType_URI &&
         filetype != FileType_URL
       )
    {
      /* Not a URI or URL file, so text, HTML, image... */

      StrNCpy0(url, m->data.data_load.leaf_name);
      urlutils_pathname_to_url(url, sizeof(url));

      /* For text and HTML, we can use an internal URL scheme */
      /* to allow the fetcher to load the scrap file and then */
      /* remove it, as it should. For other types, the main   */
      /* fetcher modules must be used; so we can't get rid    */
      /* of the scrap file. If something else trashes it,     */
      /* tough.                                               */

      if (
           apptoapp &&
           (
             filetype == FileType_TEXT ||
             filetype == FileType_HTML
           )
         )
      {
        StrNCpy0(url, Internal_URL ForScrapFile ":");

        /* Append the application suggested leafname */

        if (!data_save_suggested_leafname || !*data_save_suggested_leafname)
        {
          if (strlen(url) + 9 <= sizeof(url)) strcat(url, "HTMLfile");
        }
        else
        {
          if (strlen(url) + strlen(data_save_suggested_leafname) + 1 <= sizeof(url)) strcat(url, data_save_suggested_leafname);
        }

        /* Don't need a leafname record now */

        if (data_save_suggested_leafname)
        {
          free(data_save_suggested_leafname);
          data_save_suggested_leafname = NULL;
        }
      }
    }
    else
    {
      urlutils_load_uri_file(url, sizeof(url), title, sizeof(title), m->data.data_load.leaf_name);

      /* Delete scrap if used - we've finished with the */
      /* file now, certainly.                           */

      if (apptoapp) remove(m->data.data_load.leaf_name);
    }

    /* Take appropriate action */

    if (m->data.data_load.destination_window <= 0)
    {
      /* Load file to icon bar - i.e. open a new window. */

      ChkError(windows_create_browser(url, NULL, NULL, NULL, Windows_CreateBrowser_Normal));
    }

    #ifndef REMOTE_HOTLIST

      else
      {
        int handled = 0;

        /* We allow two places for loading - a browser window (or bits of it), */
        /* and for URI / URL files, the hotlist window. Check the latter.      */

        if (
             filetype == FileType_URI ||
             filetype == FileType_URL
           )
        {
          ObjectId this;

          /* If we can get the window object ID... */

          if (
               !window_wimp_to_toolbox(0,
                                       m->data.data_load.destination_window,
                                       -1,
                                       &this,
                                       NULL)
             )
          {
            /* ...and it matches that of the hotlist, add the item. */

            if (this == hotlist_return_window_id())
            {
              char * leaf = NULL;

              /* If we have a title for the URL, use it, else try other methods... */

              if (!*title)
              {
                /* For the description, use the source leafname, unless this */
                /* is from another app (your_ref is non-zero), in which case */
                /* use the URL, as <Wimp$Scrap> isn't very friendly or       */
                /* unique.                                                   */

                if (!m->hdr.your_ref) leaf = strrchr(m->data.data_load.leaf_name, '.');

                if (!leaf) leaf = url;
                else       leaf ++;
              }

              ChkError(hotlist_add_position(m->data.data_load.destination_x,
                                            m->data.data_load.destination_y,
                                            *title ? title : leaf,
                                            url));

              handled = 1;
            }
          }
        }

        /* We can also load HTML files, in a fashion, to the hotlist. */

        if (filetype == FileType_HTML)
        {
          ObjectId this;

          /* If we can get the window object ID... */

          if (
               !window_wimp_to_toolbox(0,
                                       m->data.data_load.destination_window,
                                       -1,
                                       &this,
                                       NULL)
             )
          {
            /* ...and it matches that of the hotlist, add the item. */

            if (this == hotlist_return_window_id())
            {
              char * path = m->data.data_load.leaf_name;

              ChkError(hotlist_add_html_file(m->data.data_load.destination_x,
                                             m->data.data_load.destination_y,
                                             path));

              handled = 1;
            }
          }
        }

        /* If not handled yet, deal with the file */

        if (!handled)
        {
          browser_data * b = NULL;

          /* Otherwise, load file to a browser window. Need to find */
          /* its browser_data structure for this.                   */

          utils_browser_from_window(m->data.data_load.destination_window, &b);

          if (b && !b->small_fetch) ChkError(fetchpage_new(b, url, 1, 0));
        }
      }

    #endif

    ChkError(protocols_atatl_send_data_load_ack(m));
  }

  return NULL;
}

/*************************************************/
/* protocols_atatl_send_data_load_ack()          */
/*                                               */
/* In response to a Message_DataLoad, reply with */
/* a Message_DataLoadAck.                        */
/*                                               */
/* See PRM 3-252 and 254.                        */
/*                                               */
/* Parameters: Pointer to the WimpMessage struct */
/*             for the received message. The     */
/*             contents will be modified and     */
/*             used in a reply.                  */
/*************************************************/

_kernel_oserror * protocols_atatl_send_data_load_ack(WimpMessage * m)
{
  #ifdef TRACE
    if (tl & (1u<<28)) Printf("protocols_atatl_send_data_load_ack: Called\n");
  #endif

  m->hdr.your_ref    = m->hdr.my_ref;
  m->hdr.action_code = Wimp_MDataLoadAck;

  return wimp_send_message(Wimp_EUserMessage, m, m->hdr.sender, 0, NULL);
}

/*************************************************/
/* protocols_atatl_got_data_save()               */
/*                                               */
/* Handle reception of a Message_DataSave - load */
/* data from another application. Handles RAM    */
/* transfer for some filetypes.                  */
/*                                               */
/* See PRM 3-252, 253, 255 and 256.              */
/*                                               */
/* Parameters: Pointer to the WimpMessage struct */
/*             for the received message. The     */
/*             contents will be modified and     */
/*             used in a reply.                  */
/*************************************************/

_kernel_oserror * protocols_atatl_got_data_save(WimpMessage * m)
{
  _kernel_oserror * e        = NULL;
  int               filetype = m->data.data_save.file_type;
  int               ram      = 0;
  WimpMessage     * reply;
  ComponentId       c;

  #ifdef TRACE
    if (tl & (1u<<28)) Printf("protocols_atatl_got_data_save: Called\n");
  #endif

  /* Was the URL writable targetted? In this case, if it was a text file, */
  /* assume it contained a URL - modify the Wimp Message block to contain */
  /* an ANT URL file filetype.                                            */

  RetError(window_wimp_to_toolbox(0,
                                  m->data.data_save.destination_window,
                                  m->data.data_save.destination_icon,
                                  NULL,
                                  &c));

  if (c == URLBarWrit && filetype == FileType_TEXT) filetype = FileType_URL;

  /* Can we do RAM transfer for this file type? */

  if (
       filetype == FileType_URI ||
       filetype == FileType_URL
     )
     ram = 1;

  /* Can't do anything if <Wimp$Scrap> isn't defined and we want to */
  /* use it for file transfer. Note that the checking function will */
  /* raise an appropriate error in passing ('<Wimp$Scrap> not       */
  /* defined', for example).                                        */

  if (!ram && utils_check_scrap()) return NULL;

  /* Free any allocated data left over from previous calls */

  if (pending_data_save_ack)
  {
    #ifdef TRACE
      if (tl & (1u<<28)) Printf("protocols_atatl_got_data_save: Freeing old pending Acknowledge block\n");
    #endif

    free(pending_data_save_ack);
    pending_data_save_ack = NULL;
  }

  if (data_save_suggested_leafname)
  {
    #ifdef TRACE
      if (tl & (1u<<28)) Printf("protocols_atatl_got_data_save: Freeing old suggested leafname block\n");
    #endif

    free(data_save_suggested_leafname);
    data_save_suggested_leafname = NULL;
  }

  /* Record the leafname. Thus, when the Message_DataLoad comes along  */
  /* or the RAM transfer is finished, we can use this recorded leaf in */
  /* some meaningful place (such as the URL bar). Otherwise, we have   */
  /* no record (only e.g. '<Wimp$Scrap>' in a Message_DataLoad) and    */
  /* things get a bit more ugly at the front-end.                      */

  if (*m->data.data_save.leaf_name)
  {
    /* Allocate the block */

    data_save_suggested_leafname = malloc(strlen(m->data.data_save_ack.leaf_name) + 1);

    /* Do nothing if this should fail; assuming we managed  */
    /* to load the file, allow the load routines to default */
    /* down to a general alternative in the absence of this */
    /* record.                                              */

    if (data_save_suggested_leafname) strcpy(data_save_suggested_leafname, m->data.data_save_ack.leaf_name);
  }

  /* Deal with RAM transfer - send out a RAMFetch. If this */
  /* bounces, we'll need to drop back to scrap files.      */

  if (ram)
  {
    /* First, we may need to use file transfer so fill in */
    /* a pending DataSaveAck block for later use.         */

    pending_data_save_ack = malloc(sizeof(WimpMessage));

    if (!pending_data_save_ack) return make_no_memory_error(11);

    #ifdef TRACE
      if (tl & (1u<<28)) Printf("protocols_atatl_got_data_save: Allocated new pending Acknowledge block\n");
    #endif

    reply = pending_data_save_ack;
  }
  else
  {
    /* Otherwise, just point back to the existing message and modify */
    /* its contents for a reply in the normal fashion.               */

    reply = m;
  }

  /* Fill it in */

  *reply = *m;

  reply->hdr.your_ref    = m->hdr.my_ref;
  reply->hdr.action_code = Wimp_MDataSaveAck;

  reply->data.data_save_ack.estimated_size = -1;

  /* Write the filename for Scrap to the message block */
  /* and update the size field in the header.          */

  StrNCpy0(reply->data.data_save_ack.leaf_name, Save_ScrapFile);

  reply->hdr.size = (int) WordAlign(strlen(Save_ScrapFile) + 45); /* (44 for stuff up to the pathname, plus 1 for terminator) */

  /* For RAM transfer, send a Message_RAMFetch */

  if (ram)
  {
    WimpMessage * raf = m;

    /* Free any existing RAM transfer buffer and allocate a new one */

    if (rtb) free(rtb);
    rtb = malloc(RTB_Size);

    /* If the allocation fails, drop through to a scrap file method */

    if (rtb)
    {
      /* Fill in the message details */

      #ifdef TRACE
        malloccount += RTB_Size;
        if (tl & (1u<<13)) Printf("** malloccount (protocols_atatl_got_data_save): \0211%d\0217\n",malloccount);
      #endif

      raf->hdr.size                   = 28;
      raf->hdr.your_ref               = m->hdr.my_ref;
      raf->hdr.action_code            = Wimp_MRAMFetch;

      raf->data.ram_fetch.buffer      = rtb;
      raf->data.ram_fetch.buffer_size = RTB_Size;

      /* Send it */

      #ifdef TRACE
        if (tl & (1u<<28)) Printf("protocols_atatl_got_data_save: Sending Message_RAMFetch in response\n");
      #endif

      RetError(wimp_send_message(Wimp_EUserMessageRecorded,
                                 raf,
                                 m->hdr.sender,
                                 0,
                                 NULL));

      /* Remember my_ref in case it bounces */

      ram_fetch_reference = raf->hdr.my_ref;

      /* Flag that we've not started transfer of actual data yet */

      ram_load_started = 0;
    }
    else ram = 0; /* So that the Message_DataSaveAck goes out instead */
  }

  /* Otherwise send a Message_DataSaveAck reply */

  if (!ram) /* Must use this as the RAM transfer code will set 'ram' to 0 if it can't claim a transfer buffer */
  {
    RetError(wimp_send_message(Wimp_EUserMessage, /* Not interested in it bouncing */
                               reply,
                               m->hdr.sender,
                               0,
                               NULL));
  }

  return e;
}

/*************************************************/
/* protocols_atatl_ram_fetch_bounced()           */
/*                                               */
/* Following getting a Message_DataSave,         */
/* protocols_atatl_got_data_save may send out a  */
/* Message_RAMFetch. If this bounces, this       */
/* function is called. If the bounce is indeed   */
/* due to the RAMFetch we sent, then send out    */
/* a Message_DataSaveAck instead, to try and use */
/* scrap file transfer. This is stored in the    */
/* 'pending' block by the same function that     */
/* sends the Message_RAMFetch.                   */
/*                                               */
/* Alternatively, a Message_RAMFetch being sent  */
/* in reply to a Message_RAMTransmit *during*    */
/* RAM transfer could bounce, and this function  */
/* would be called. In that case, we should      */
/* abort data transfer. The other end is meant   */
/* to raise an error (which is odd, since it     */
/* seems likely that it will have died - since   */
/* it's sending, the changes of running out of   */
/* memory at that end are slim).                 */
/*                                               */
/* See PRM 3-255.                                */
/*                                               */
/* Parameters: Pointer to the WimpMessage struct */
/*             for the received message.         */
/*************************************************/

_kernel_oserror * protocols_atatl_ram_fetch_bounced(WimpMessage * m)
{
  #ifdef TRACE
    if (tl & (1u<<28)) Printf("protocols_atatl_ram_fetch_bounced: Called\n");
  #endif

  if (m->hdr.my_ref == ram_fetch_reference)
  {
    if (!ram_load_started && pending_data_save_ack)
    {
      /* The bounce is from the first Message_RAMFetch, so try using */
      /* scrap file transfer instead.                                */

      _kernel_oserror * e;

      #ifdef TRACE
        if (tl & (1u<<28)) Printf("protocols_atatl_ram_fetch_bounced: Recognised bounce for a new transfer - using Scrap instead\n");
      #endif

      /* Before doing this, make sure Scrap is present - the call */
      /* will raise an error if not, before returning here        */

      if (utils_check_scrap()) return NULL;

      /* Send the pending message */

      e = wimp_send_message(Wimp_EUserMessage,
                            pending_data_save_ack,
                            pending_data_save_ack->hdr.sender,
                            0,
                            NULL);

      /* Free the block and return any error that may have been generated */

      free(pending_data_save_ack);
      pending_data_save_ack = NULL;

      return e;
    }
    else
    {
      #ifdef TRACE
        if (tl & (1u<<28)) Printf("protocols_atatl_ram_fetch_bounced: Recognised bounce for an ongoing transfer - aborting\n");
      #endif

      /* The bounce is from an ongoing RAM transfer, so abort that */
      /* transfer - free any in-use buffers.                       */

      if (ram_load_uri_buffer)
      {
        free(ram_load_uri_buffer);
        ram_load_uri_buffer = NULL;

        #ifdef TRACE
          malloccount -= ram_load_buffer_size;
          if (tl & (1u<<13)) Printf("** malloccount (protocols_atatl_ram_fetch_bounced): \0212%d\0217\n",malloccount);
        #endif
      }

      ram_load_buffer_size = 0;

      if (rtb)
      {
        free(rtb);
        rtb = NULL;

        #ifdef TRACE
          malloccount -= RTB_Size;
          if (tl & (1u<<13)) Printf("** malloccount (protocols_atatl_ram_fetch_bounced): \0212%d\0217\n",malloccount);
        #endif
      }

      /* Free the pending Message_DataSaveAck block, if present */

      if (pending_data_save_ack)
      {
        free(pending_data_save_ack);
        pending_data_save_ack = 0;
      }
    }
  }

  return NULL;
}

/*************************************************/
/* protocols_atatl_got_ram_transmit()            */
/*                                               */
/* Deals with a Message_RAMTransmit - we have    */
/* some data in the 'rtb' buffer.                */
/*                                               */
/* See PRM 3-255, 256.                           */
/*                                               */
/* Parameters: Pointer to the WimpMessage struct */
/*             for the received message. The     */
/*             contents will be modified and     */
/*             used in a reply.                  */
/*************************************************/

_kernel_oserror * protocols_atatl_got_ram_transmit(WimpMessage * m)
{
  _kernel_oserror * e        = NULL;
  int               ok       = 1;
  int               finished = 0;

  #ifdef TRACE
    if (tl & (1u<<28)) Printf("protocols_atatl_got_ram_transmit: Called with %d bytes to receive\n", m->data.ram_transmit.nbytes);
  #endif

  /* Flag that transfer is in progress */

  ram_load_started = 1;

  /* Flag if we've finished, too */

  if (m->data.ram_transmit.nbytes < RTB_Size) finished = 1;
  else if (m->data.ram_transmit.nbytes > RTB_Size) m->data.ram_transmit.nbytes = RTB_Size; /* You *never* know...! */

  /* We should know where the buffer is */

  #ifdef TRACE

    if (m->data.ram_transmit.buffer != rtb)
    {
      erb.errnum = Utils_Error_Custom_Normal;

      sprintf(erb.errmess,
              "Expected to get buffer %p but got %p instead, in protocols_atatl_got_ram_transmit",
              rtb,
              m->data.ram_transmit.buffer);

      e  = &erb;
      ok = 0;
    }

  #else

    if (m->data.ram_transmit.buffer != rtb) ok = 0;

  #endif

  /* The action here depends on the type of file being transferred; */
  /* we know from here on in that the 'rtb' buffer is the buffer    */
  /* that has been filled in (see above), so we can use this rather */
  /* than the more cumbersome 'm->data.ram_transmit.buffer'.        */

  if (ok && pending_data_save_ack)
  {
    ComponentId c;
    int         filetype = pending_data_save_ack->data.data_save_ack.file_type;

    /* Was this dragged to the URL writable, and thus should be */
    /* treated as an ANT URL file (if text)?                    */

    RetError(window_wimp_to_toolbox(0,
                                    pending_data_save_ack->data.data_save_ack.destination_window,
                                    pending_data_save_ack->data.data_save_ack.destination_icon,
                                    NULL,
                                    &c));

    if (c == URLBarWrit && filetype == FileType_TEXT) filetype = FileType_URL;

    switch (filetype)
    {
      /* For URI / URL files, copy data from the file transfer */
      /* buffer (which may deliver the file in several small   */
      /* chunks) to a cumulative file buffer, and then process */
      /* that whole thing when finished.                       */

      case FileType_URL: /* Same as for URI files, so no 'break' */
      case FileType_URI:
      {
        int first = 0;

        /* Only proceed with transfer if there are some bytes to get  */
        /* (otherwise, this was the last message with the final chunk */
        /* of data having exactly filled the buffer last time - so    */
        /* act upon the buffer contents now, without trying to        */
        /* transfer zero bytes first).                                */

        if (m->data.ram_transmit.nbytes)
        {
          /* Allocate or extend the local buffer to take in the URI file */

          if (!ram_load_uri_buffer)
          {
            /* Note we allocate 1 byte more than needed to allow a terminator to */
            /* be inserted after the URL string when the transfer has ended;     */
            /* this is because a URI file may end simply by EOF, it does not     */
            /* need a terminator (and in any case, that terminator may be a      */
            /* control char such as ASCII 10 or 13, which C would not treat as a */
            /* string terminator).                                               */

            ram_load_uri_buffer = malloc(m->data.ram_transmit.nbytes + 1);
            first               = 1;

            /* Complain if the allocation fails */

            if (!ram_load_uri_buffer)
            {
              e = make_no_memory_error(7);

              ok = 0;
            }

            #ifdef TRACE

              else
              {
                malloccount += m->data.ram_transmit.nbytes + 1;
                if (tl & (1u<<13)) Printf("** malloccount (protocols_atatl_got_ram_transmit): \0211%d\0217\n",malloccount);
              }

            #endif
          }
          else
          {
            void * local;

            /* realloc to a new size */

            local = realloc(ram_load_uri_buffer, ram_load_buffer_size + m->data.ram_transmit.nbytes);

            if (!local)
            {
              /* If the allocation fails, free the whole thing */

              free(ram_load_uri_buffer);
              ram_load_uri_buffer = NULL;

              #ifdef TRACE
                malloccount -= ram_load_buffer_size;
                if (tl & (1u<<13)) Printf("** malloccount (protocols_atatl_got_ram_transmit): \0212%d\0217\n",malloccount);
              #endif

              ram_load_buffer_size = 0;

              e = make_no_memory_error(7);

              ok = 0;
            }

            /* Otherwise record a possibly new buffer address */

            else
            {
              ram_load_uri_buffer = local;

              #ifdef TRACE
                malloccount += m->data.ram_transmit.nbytes;
                if (tl & (1u<<13)) Printf("** malloccount (protocols_atatl_got_ram_transmit): \0211%d\0217\n",malloccount);
              #endif
            }
          }

          if (ok)
          {
            /* Copy the data from the file transfer buffer into */
            /* the cumulative URI file buffer                   */

            memcpy(ram_load_uri_buffer + ram_load_buffer_size - !first,
                   rtb,
                   m->data.ram_transmit.nbytes);

            /* Increment the buffer size counter, remembering to add 1 if this */
            /* is the first time we've allocated data, to allow room for a     */
            /* string terminator when the transfer finishes.                   */

            ram_load_buffer_size += m->data.ram_transmit.nbytes + !!first;

            /* If tracing the process of the transfer, show the buffer contents */
            /* (but don't bother showing the uninitialised last byte).          */

            #ifdef TRACE
              if (tl & (1u<<28)) trace_dump_buffer(ram_load_uri_buffer,
                                                   ram_load_buffer_size - 1,
                                                   2);
            #endif
          }
        }

        /* If the transfer has finished, fetch the URL */

        if (finished)
        {
          /* Remember we allocated 1 byte more than needed at the start, */
          /* so that this terminator could be inserted. This is required */
          /* for URL files, and won't hurt for URI files.                */

          ram_load_uri_buffer[ram_load_buffer_size - 1] = 0;

          /* This is capable of dealing with URI or URL files */

          urlutils_extract_uri(ram_load_uri_buffer, ram_load_buffer_size);

          if (pending_data_save_ack->data.data_save_ack.destination_window <= 0)
          {
            /* The original Message_DataSave was for no specific window */

            e = windows_create_browser(ram_load_uri_buffer, NULL, NULL, NULL, Windows_CreateBrowser_Normal);
          }

          #ifndef REMOTE_HOTLIST

            else
            {
              /* As with DataLoad, this can go to a Hotlist window or a browser; */
              /* the hotlist is the simpler case, check that first.              */

              ObjectId this;
              int      handled = 0;

              /* If we can get the window object ID... */

              if (
                   !window_wimp_to_toolbox(0,
                                           pending_data_save_ack->data.data_save_ack.destination_window,
                                           -1,
                                           &this,
                                           NULL)
                 )
              {
                /* ...and it matches that of the hotlist, add the item. */

                if (this == hotlist_return_window_id())
                {
                  /* Use the title, if we have one */

                  if (*(ram_load_uri_buffer + strlen(ram_load_uri_buffer) + 1))
                  {
                    ChkError(hotlist_add_position(pending_data_save_ack->data.data_save_ack.destination_x,
                                                  pending_data_save_ack->data.data_save_ack.destination_y,
                                                  ram_load_uri_buffer + strlen(ram_load_uri_buffer) + 1,
                                                  ram_load_uri_buffer));
                  }
                  else
                  {
                    ChkError(hotlist_add_position(pending_data_save_ack->data.data_save_ack.destination_x,
                                                  pending_data_save_ack->data.data_save_ack.destination_y,
                                                  ram_load_uri_buffer,
                                                  ram_load_uri_buffer));
                  }

                  handled = 1;
                }
              }

              if (!handled)
              {
                browser_data * b;

                /* The message was for a specific window, so find out which one */

                utils_browser_from_window(pending_data_save_ack->data.data_save_ack.destination_window, &b);

                if (b && !b->small_fetch) e = fetchpage_new(b, ram_load_uri_buffer, 1, 0);
              }
            }

          #endif

          free (ram_load_uri_buffer);
          ram_load_uri_buffer = NULL;

          #ifdef TRACE
            malloccount -= ram_load_buffer_size;
            if (tl & (1u<<13)) Printf("** malloccount (protocols_atatl_got_ram_transmit): \0212%d\0217\n",malloccount);
          #endif

          ram_load_buffer_size = 0;

          if (!e) ok = 0;
        }
      }
      break;

      default:
      {
        /* If we don't recognise the filetype, something is very wrong... */

        #ifdef TRACE

          erb.errnum = Utils_Error_Custom_Normal;
          sprintf(erb.errmess,
                  "Unrecognised filetype 0x%03x (%x) in protocols_atatl_got_ram_transmit",
                  pending_data_save_ack->data.data_save_ack.file_type,
                  pending_data_save_ack->data.data_save_ack.file_type);

          e  = &erb;
          ok = 0;

        #else

          ok = 0;

        #endif
      }
      break;
    }
  }

  if (ok && !finished)
  {
    /* If things are OK still, send a RAMFetch to get the  */
    /* next chunk, provided that there is more to get.     */
    /*                                                     */
    /* If things are not OK, no reply is given, thereby    */
    /* abandoning the transfer (PRM 3-256).                */

    WimpMessage * raf = m;

    raf->hdr.size        = 28;
    raf->hdr.your_ref    = m->hdr.my_ref;
    raf->hdr.action_code = Wimp_MRAMFetch;

    #ifdef TRACE
      if (tl & (1u<<28)) Printf("protocols_atatl_got_ram_transmit: Sending RAMFetch in reply\n");
    #endif

    e = wimp_send_message(Wimp_EUserMessageRecorded,
                          raf,
                          m->hdr.sender,
                          0,
                          NULL);

    ram_fetch_reference = m->hdr.my_ref;
  }

  /* Either we've finished or there was an error higher up */

  if (finished || !ok)
  {
    /* Free the RAM transfer buffer, if required. */

    if (rtb) free(rtb);
    rtb = NULL;

    #ifdef TRACE
      malloccount -= RTB_Size;
      if (tl & (1u<<13)) Printf("** malloccount (protocols_atatl_got_ram_transmit): \0212%d\0217\n",malloccount);
    #endif

    /* Free any pending Message_DataSaveAck */

    if (pending_data_save_ack)
    {
      #ifdef TRACE
        if (tl & (1u<<28)) Printf("protocols_atatl_got_ram_transmit: Freeing old pending Acknowledge block\n");
      #endif

      free(pending_data_save_ack);
      pending_data_save_ack = NULL;
    }
  }

  return e;
}

/*************************************************/
/* protocols_pp_send_print_save()                */
/*                                               */
/* First stage of the Print Protocol - broadcast */
/* a Message_PrintSave.                          */
/*                                               */
/* See PRM 3-259, 262.                           */
/*************************************************/

_kernel_oserror * protocols_pp_send_print_save(void)
{
  WimpMessage m;

  /* Fill in the header */

  m.hdr.your_ref    = 0;
  m.hdr.action_code = Browser_Message_PrintSave;

  /* Fill in the body */

  m.data.data_save.destination_window = 0;
  m.data.data_save.destination_icon   = 0;
  m.data.data_save.destination_x      = 0;
  m.data.data_save.destination_y      = 0;
  m.data.data_save.estimated_size     = 4096;
  m.data.data_save.file_type          = FileType_POUT;

  StrNCpy0(m.data.data_save.leaf_name,
           lookup_token("PrintName:WebPage",0,0));

  /* Work out the message size */

  m.hdr.size = (strlen(m.data.data_save.leaf_name) + 44);
  if (m.hdr.size & 3) m.hdr.size = (m.hdr.size & ~3) + 4;

  /* Send the message */

  RetError(wimp_send_message(Wimp_EUserMessageRecorded,
                             &m,
                             0,
                             0,
                             NULL));

  /* A Message_DataSaveAck returns, so to distinguish this from */
  /* any other Message_DataSaveAck, remember my_ref and check   */
  /* it against the returned your_ref in future.                */

  print_save_reference = m.hdr.my_ref;

  return NULL;
}

/*************************************************/
/* protocols_pp_print_save_bounced()             */
/*                                               */
/* If a Message_PrintSave bounces, the browser   */
/* tried to use the printer protocol to do a     */
/* print job but the printer wasn't loaded. In   */
/* that case, print directly to the 'printer:'   */
/* device.                                       */
/*                                               */
/* See PRM 3-259 for details of where this fits  */
/* into the protocol, or 3-262 for the message   */
/* structure.                                    */
/*                                               */
/* Parameters: Pointer to the WimpMessage struct */
/*             for the received message.         */
/*************************************************/

_kernel_oserror * protocols_pp_print_save_bounced(WimpMessage * m)
{
  if (m->hdr.my_ref && m->hdr.my_ref == print_save_reference)
  {
    print_save_reference = 0;

    /* Go for it. */

    print_print(NULL);
  }

  #ifdef TRACE

    else
    {
      erb.errnum = Utils_Error_Custom_Normal;

      sprintf(erb.errmess,
              "Message reference %x not recognised in protocols_pp_print_save_bounced",
              m->hdr.your_ref);

      return &erb;
    }

  #endif

  return NULL;
}

/*************************************************/
/* protocols_pp_got_print_error()                */
/*                                               */
/* Handle reception of a Message_PrintError -    */
/* report an error from the printer driver.      */
/*                                               */
/* See PRM 3-262.                                */
/*                                               */
/* Parameters: Pointer to the WimpMessage struct */
/*             for the received message.         */
/*************************************************/

_kernel_oserror * protocols_pp_got_print_error(WimpMessage * m)
{
  if (m->hdr.size == 20)
  {
    /* RISC OS 2 printer manager's PrintBusy response */

    erb.errnum = Utils_Error_Custom_Message;

    StrNCpy0(erb.errmess,
             lookup_token("PrintBusy:The printer is currently busy.",
                          0,
                          0));

    show_error_ret(&erb);
  }

  /* RISC OS 3 !Printers-generated specific error response */

  else show_error_ret((_kernel_oserror *) &m->data);

  return NULL;
}

/*************************************************/
/* protocols_pp_got_data_save_ack()              */
/*                                               */
/* Handle reception of a Message_DataSaveAck     */
/* where the your_ref field shows that it has    */
/* been sent in relation to a print job. Only    */
/* call in those circumstances...                */
/*                                               */
/* See PRM 3-259 / 3-260 for details of where    */
/* this fits into the protocol, or PRM 3-253     */
/* for details on the message structure.         */
/*                                               */
/* Parameters: Pointer to the WimpMessage struct */
/*             for the received message. The     */
/*             contents will be modified and     */
/*             used in a reply.                  */
/*************************************************/

static _kernel_oserror * protocols_pp_got_data_save_ack(WimpMessage * m)
{
  _kernel_oserror * e;
  WimpMessage     * dl = m;
  int               file_size;

  #ifdef TRACE
    if (tl & (1u<<28)) Printf("protocols_pp_got_data_save_ack: Called\n");
  #endif

  /* Print to a file in the !Printers-given file, then send */
  /* a DataLoad to the printer manager to tell it to put    */
  /* the file into the print queue.                         */

  print_save_reference = 0;

  print_print(m->data.data_save.leaf_name);

  e = _swix(OS_File,
        _INR(0,1) | _OUT(4),

        23, /* Read catalogue info */
        m->data.data_save.leaf_name,

        &file_size);

  if (e) return e;

  dl->hdr.size        = 64;
  dl->hdr.your_ref    = m->hdr.my_ref;
  dl->hdr.action_code = Wimp_MDataLoad;

  dl->data.data_load.estimated_size = file_size;
  dl->data.data_load.file_type      = FileType_POUT;

  e = _swix(OS_File,
            _INR(0,2),

            18, /* Set filetype of named object */
            m->data.data_save_ack.leaf_name,
            FileType_POUT);

  strcpy(dl->data.data_load.leaf_name, m->data.data_save_ack.leaf_name);

  /* Send the DataLoad - a UserMessage, not UserMessageRecorded, */
  /* so it can't bounce back.                                    */

  #ifdef TRACE
    if (tl & (1u<<28)) Printf("protocols_pp_got_data_save_ack: Sending Message_DataLoad in response\n");
  #endif

  return wimp_send_message(Wimp_EUserMessage,
                           dl,
                           m->hdr.sender,
                           0,
                           NULL);
}

/*************************************************/
/* protocols_pp_got_print_type_odd()             */
/*                                               */
/* Handle reception of a Message_PrintTypeOdd.   */
/*                                               */
/* If this is sent directly to the task, we can  */
/* print immediately to 'printer:'.              */
/*                                               */
/* The use of an Alias$PrintType_FF4 system      */
/* variable makes it unnecessary to deal with    */
/* the case of a broadcast PrintTypeOdd. This    */
/* happens when a queued PrintOut file that the  */
/* browser has been asked to print rises to the  */
/* top of !Printers' queue. Bizarrely, !Printers */
/* doesn't recognise the filetype and raises the */
/* message. We allow this to fall back to the    */
/* system variable, which copies the PrintOut    */
/* file to the 'printer:' device.                */
/*                                               */
/* This is a hole in an otherwise workable, if   */
/* cumbersome, protocol.                         */
/*                                               */
/* See PRM 3-263.                                */
/*                                               */
/* Parameters: Pointer to the WimpMessage struct */
/*             for the received message. The     */
/*             contents will be modified and     */
/*             used in a reply.                  */
/*************************************************/

_kernel_oserror * protocols_pp_got_print_type_odd(WimpMessage * m)
{
  WimpMessage * ptk = m;

  if (m->hdr.your_ref && m->hdr.your_ref == print_save_reference)
  {
    /* The printer manager sent PrintTypeOdd as a reply to this */
    /* task (not a broadcast), so go ahead and print.           */

    print_save_reference = 0;

    /* Send PrintTypeKnown */

    ptk->hdr.size        = 20;
    ptk->hdr.your_ref    = m->hdr.my_ref;
    ptk->hdr.action_code = Browser_Message_PrintTypeKnown;

    RetError(wimp_send_message(Wimp_EUserMessage,
                               ptk,
                               m->hdr.sender,
                               0,
                               NULL));

    print_print(NULL);
  }

  #ifdef TRACE

    else if (m->hdr.your_ref)
    {
      erb.errnum = Utils_Error_Custom_Normal;

      sprintf(erb.errmess,
              "Message reference %x unrecognised in protocols_pp_got_print_type_odd",
              m->hdr.your_ref);

      return &erb;
    }

  #endif

// Commented out as the Alias$@PrintType_FF4 system variable does this job
// anyway, and if we don't claim this message then anything else which may
// have a better idea of what to do at least gets a chance to try.
//
// This currently doesn't work, incidentally; the conditions on the 'if'
// are wrong (print_save_ref has probably been set to 0, but I never
// got the chance to properly debug this before removing it due to time
// constraints...).
//
//   else if (print_save_ref && m->data.data_save.file_type == FileType_POUT)
//   {
//     /* If the printer doesn't understand PrintOut files, then */
//     /* it may be broken (!) / PostScript. So reply, and copy  */
//     /* the file to the printer device directly.               */
//
//     print_save_ref = 0;
//
//     ptk->hdr.size        = 20;
//     ptk->hdr.your_ref    = m->hdr.my_ref;
//     ptk->hdr.action_code = Browser_Message_PrintTypeKnown;
//
//     ChkError(wimp_send_message(Wimp_EUserMessage, ptk, m->hdr.sender, 0, NULL));
//
//     _swix(OS_FSControl,
//           _INR(0,3),
//
//           26,
//           m->data.data_save.leaf_name,
//           "printer:",
//           2); /* Flags - 'Force' set, but no others. */
//   }

  return NULL;
}

/*************************************************/
/* protocols_auh_got_process()                   */
/*                                               */
/* Handle reception of a URI_MProcess - process  */
/* a URI from the URI handler, replying if we    */
/* can handle the URI.                           */
/*                                               */
/* See 1307,260/FS.                              */
/*                                               */
/* Parameters: Pointer to the WimpMessage struct */
/*             for the received message. The     */
/*             contents will be modified and     */
/*             used in a reply.                  */
/*************************************************/

_kernel_oserror * protocols_auh_got_process(WimpMessage * m)
{
  URIProcessMessage * uri    = (URIProcessMessage *) &m->data;
  int                 ok;
  unsigned int        sender = m->hdr.sender;

  /* Can we handle this URI? */

  ok = urlutils_check_protocols(uri->uri);

  #ifdef TRACE
    if (tl & (1u<<21)) Printf("protocols_auh_got_process: URI_MProcess '%s', ok = %d\n",uri->uri,ok);
  #endif

  /* If so, reply to the message and possibly start a fetch */

  if (ok)
  {
    #ifndef SINGLE_USER

      if (logged_in)
      {

    #endif

    /* Only fetch if the flags bits don't say we're to just */
    /* check the URI could be handled.                      */

    if (!uri->flags.bits.check)
    {
      uri_queue * entry = urlutils_find_queue_entry(uri->uri_handle);

      if (entry)
      {
        RetError(fetchpage_postprocess_uri(entry->b,
                                           uri  ->uri,
                                           entry->flags & URIQueue_RecordInHistory ? 1 : 0));

        /* Don't remove it from the queue of uri_queue structures yet - */
        /* wait for the ReturnResult message for that.                  */
      }

      else RetError(windows_create_browser(uri->uri, NULL, NULL, NULL, Windows_CreateBrowser_Normal));
    }

    #ifndef SINGLE_USER

      }
      else
      {
        erb.errnum = Utils_Error_Custom_Message;

        StrNCpy0(erb.errmess,
                 lookup_token("MustLogIn:The browser cannot fetch Web pages until you log in.",
                              0,
                              0));

        show_error_ret(&erb);
      }

    #endif

    /* Now reply, saying that we've handled the message */

    m->hdr.sender      = task_handle;
    m->hdr.your_ref    = m->hdr.my_ref;
    m->hdr.action_code = URI_MProcessAck;

    RetError(wimp_send_message(Wimp_EUserMessage,
                               m,
                               sender,
                               0,
                               NULL));
  }

  return NULL;
}

/*************************************************/
/* protocols_auh_got_return_result()             */
/*                                               */
/* Handle reception of a URI_MReturnResult -     */
/* the URI handler is reporting what happened to */
/* a URI we dispatched through it.               */
/*                                               */
/* See 1307,260/FS.                              */
/*                                               */
/* Parameters: Pointer to the WimpMessage struct */
/*             for the received message. The     */
/*             contents will be modified and     */
/*             used in a reply.                  */
/*************************************************/

_kernel_oserror * protocols_auh_got_return_result(WimpMessage * m)
{
  URIReturnResultMessage * uri = (URIReturnResultMessage *) &m->data;

  #ifdef TRACE
    if (tl & (1u<<21)) Printf("protocols_auh_got_return_result: URI_MReturnResult, not_claimed = %d\n",uri->flags.bits.not_claimed);
  #endif

  /* Remove the entry from the queue */

  RetError(urlutils_remove_from_queue(uri->uri_handle));

  /* If the URI was not claimed by anyone, give an appropriate error */

  if (uri->flags.bits.not_claimed)
  {
    erb.errnum = Utils_Error_Custom_Message;

    StrNCpy0(erb.errmess,
             lookup_token("CannotFetch:The browser does not have a method of fetching the requested site.",
                          0,0));

    show_error_ret(&erb);
  }

  return NULL;
}

/*************************************************/
/* protocols_auh_got_dying()                     */
/*                                               */
/* Handle reception of a URI_MDying - the URI    */
/* handler is closing down.                      */
/*                                               */
/* See 1307,260/FS.                              */
/*                                               */
/* Parameters: Pointer to the WimpMessage struct */
/*             for the received message.         */
/*************************************************/

_kernel_oserror * protocols_auh_got_dying(WimpMessage * m)
{
  /* If the URI handler is dying, don't try and use it anymore... */

  uri_module_present = 0;

  return NULL;
}

/*************************************************/
/* protocols_ih_send_help_request()              */
/*                                               */
/* Sends out HelpRequest messages for the item   */
/* the mouse pointer is currently over, every    */
/* 20 centiseconds or so.                        */
/*                                               */
/* Parameters are as standard for a Wimp NULL    */
/* event handler.                                */
/*************************************************/

int protocols_ih_send_help_request(int eventcode, WimpPollBlock * block, IdBlock * idb, void * handle)
{
  int        time_now;
  static int last_time   = 0;
  static int last_window = 0;
  static int last_icon   = 0;

  /* Only proceed if the Controls say to do so */

  if (controls.claim_help)
  {
    /* Don't sent out requests too often */

    _swix(OS_ReadMonotonicTime, _OUT(0), &time_now);

    if (time_now - last_time > 20)
    {
      WimpGetPointerInfoBlock i;
      WimpMessage             m;

      last_time = time_now;

      ChkError(wimp_get_pointer_info(&i));

      /* Don't send a request if the pointer isn't over a */
      /* browser-owned window.                            */

      if (task_handle == task_from_window(i.window_handle))
      {
        /* Don't send out multiple requests for the same window/icon. */

        if (i.icon_handle != last_icon || i.window_handle != last_window)
        {
          last_icon   = i.icon_handle;
          last_window = i.window_handle;
        }
        else return 0;

        /* Build the message block and send the request */

        m.hdr.size        = 40;
        m.hdr.sender      = task_handle;
        m.hdr.my_ref      = 0;
        m.hdr.your_ref    = 0;
        m.hdr.action_code = Wimp_MHelpRequest;

        m.data.help_request.mouse_x       = i.x;
        m.data.help_request.mouse_y       = i.y;
        m.data.help_request.buttons       = i.button_state;
        m.data.help_request.window_handle = i.window_handle;
        m.data.help_request.icon_handle   = i.icon_handle;

        ChkError(wimp_send_message(Wimp_EUserMessageRecorded,
                                   &m,
                                   i.window_handle,
                                   i.icon_handle,
                                   NULL));
      }
    }
  }

  return 0;
}

/*************************************************/
/* protocols_ih_help_request_bounced()           */
/*                                               */
/* If a Message_HelpRequest comes in as a        */
/* UserMessage_Acknowledge, then the browser     */
/* tried to send out such a message and it       */
/* bounced - there is no help on that item.      */
/* In this case, if we're displaing help items   */
/* in the status bar, return to a non-help       */
/* status string.                                */
/*                                               */
/* See PRM 3-244 for details of the message or   */
/* protocols_ih_send_help_request for the        */
/* message origin.                               */
/*                                               */
/* Parameters: Pointer to the WimpMessage struct */
/*             for the received message.         */
/*************************************************/

_kernel_oserror * protocols_ih_help_request_bounced(WimpMessage * m)
{
  if (controls.claim_help)
  {
    WimpGetPointerInfoBlock   i;
    browser_data            * b = NULL;
    ObjectId                  o, a = -1;

    RetError(wimp_get_pointer_info(&i));
    RetError(window_wimp_to_toolbox(0, i.window_handle, i.icon_handle, &o, NULL));

    /* If we can get an ancestor, the pointer is over e.g. a toolbar */
    /* - otherwise, assume it is over a browser window.              */

    toolbox_get_ancestor(0, o, &a, NULL);

    if (a)
    {
      toolbox_get_client_handle(0, a, (void *) &b);
    }
    else toolbox_get_client_handle(0, o, (void *) &b);

    /* If we haven't got a valid client handle, exit */

    if (!is_known_browser(b)) return NULL;

    /* Update the status line */

    if (b->status_help != NULL)
    {
      b->status_help = NULL;
      RetError(toolbars_cancel_status(b, Toolbars_Status_Help));
    }
  }

  return NULL;
}

/*************************************************/
/* protocols_ih_got_help_reply()                 */
/*                                               */
/* On receiving a Message_HelpReply, we may want */
/* to display the Help text in the status bar.   */
/*                                               */
/* See PRM 3-245.                                */
/*                                               */
/* Parameters: Pointer to the WimpMessage struct */
/*             for the received message.         */
/*************************************************/

_kernel_oserror * protocols_ih_got_help_reply(WimpMessage * m)
{
  static char last_help[Limits_StatusBarStatus];

  /* Don't do anything if the Controls say not to. */

  if (controls.claim_help)
  {
    WimpGetPointerInfoBlock   i;
    browser_data            * b = NULL;
    ObjectId                  o, a = 0;

    RetError(wimp_get_pointer_info(&i));
    RetError(window_wimp_to_toolbox(0, i.window_handle, i.icon_handle, &o, NULL));

    /* If we can get an ancestor, the pointer is over e.g. a toolbar */
    /* - otherwise, assume it is over a browser window.              */

    toolbox_get_ancestor(0, o, &a, NULL);

    if (a)
    {
      toolbox_get_client_handle(0, a, (void *) &b);
    }
    else toolbox_get_client_handle(0, o, (void *) &b);

    /* If we haven't got a valid client handle, exit. */

    if (!is_known_browser(b)) return NULL;

    /* If this is a small fetch window, don't display anything either */

    if (b->small_fetch) return NULL;

    /* If the text is empty, there was no help for that item, */
    /* so restore the old status display, if there was a      */
    /* help display already there.                            */

    if (!*m->data.help_reply.text)
    {
      if (b->status_help != NULL)
      {
        b->status_help = NULL;
        RetError(toolbars_cancel_status(b, Toolbars_Status_Help));
      }
    }
    else
    {
      /* Otherwise update the status bar with the help text, */
      /* if the text has changed.                            */

      if (
           !b->status_help ||
           (
             b->status_help                                  &&
             strcmp(b->status_help, m->data.help_reply.text)
           )
         )
      {
        StrNCpy0(last_help, m->data.help_reply.text);
        b->status_help = last_help;
        RetError(toolbars_update_status(b, Toolbars_Status_Help));
      }
    }
  }

  return NULL;
}

/*************************************************/
/* protocols_aub_got_open_url()                  */
/*                                               */
/* Handle ANT Open URL broadcasts.               */
/*                                               */
/* See http://www.ant.co.uk/support/tech/        */
/* notes/url.html (15 Sep 1997).                 */
/*                                               */
/* Parameters: Pointer to the WimpMessage struct */
/*             for the received message. The     */
/*             contents will be modified and     */
/*             used in a reply.                  */
/*************************************************/

_kernel_oserror * protocols_aub_got_open_url(WimpMessage * m)
{
  ant_open_url * open = (ant_open_url *) &m->data;
  int            ok   = 0;
  char         * url;
  char         * target;

  /* If 'tag' is non-NULL, the data is a URL string */

  if (open->tag) url = (char *) open, target = NULL;

  /* Otherwise, it's indirected */

  else
  {
    char * file;
    char * mimetype;

    /* Entries are either NULL, an offset into the  */
    /* message block (but we have to ensure that it */
    /* is a valid offset), or a pointer. This       */
    /* protocol sucks, big time.                    */

    if (!open->file.offset) file = NULL;
    else if (open->file.offset < 0x1800000)
    {
      if (open->file.offset >= sizeof(m->data.bytes)) file = NULL;
      else file = ((char *) open) + open->file.offset;
    }
    else file = open->file.ptr;

    if (!open->mimetype.offset) mimetype = NULL;
    else if (open->mimetype.offset < 0x1800000)
    {
      if (open->mimetype.offset >= sizeof(m->data.bytes)) mimetype = NULL;
      else mimetype = ((char *) open) + open->mimetype.offset;
    }
    else mimetype = open->mimetype.ptr;

    /* Can't handle file or mimetype requests */

    if (!file && !mimetype)
    {
      if (!open->url.offset) url = NULL;
      else if (open->url.offset < 0x1800000)
      {
        if (open->url.offset >= sizeof(m->data.bytes)) url = NULL;
        else url = ((char *) open) + open->url.offset;
      }
      else url = open->url.ptr;

      if (!open->target.offset) target = NULL;
      else if (open->target.offset < 0x1800000)
      {
        if (open->target.offset >= sizeof(m->data.bytes)) target = NULL;
        else target = ((char *) open) + open->target.offset;
      }
      else target = open->target.ptr;
    }
    else url = NULL, target = NULL;
  }

  /* Check the protocol - can we handle this? */

  if (url) ok = urlutils_check_protocols(url);

  /* If so, do so */

  if (ok)
  {
    /* Send out the URL */

    RetError(windows_create_browser(url,
                                    NULL,
                                    NULL,
                                    target,
                                    Windows_CreateBrowser_Normal));

    /* Acknowledge the message */

    m->hdr.your_ref = m->hdr.my_ref;

    return wimp_send_message(Wimp_EUserMessageAcknowledge,
                             m,
                             m->hdr.sender,
                             0,
                             NULL);
  }

  return NULL;
}

/*************************************************/
/* protocols_multi_got_data_save_ack()           */
/*                                               */
/* Got a DataSaveAck - this could be part of the */
/* print protocol, or standard file transfer     */
/* between applications.                         */
/*                                               */
/* Parameters: Pointer to the WimpMessage struct */
/*             for the received message.         */
/*************************************************/

_kernel_oserror * protocols_multi_got_data_save_ack(WimpMessage * m)
{
  if (m->hdr.your_ref && m->hdr.your_ref == print_save_reference) return protocols_pp_got_data_save_ack(m);
  else                                                            return protocols_atats_got_data_save_ack(m);
}

/*************************************************/
/* protocols_util_update_reference()             */
/*                                               */
/* When a message is sent out as part of an      */
/* ongoing conversation, this function should be */
/* called after Wimp_SendMessage to update the   */
/* internal records of known my_ref numbers.     */
/*                                               */
/* Parameters: The value of the your_ref field   */
/*             in the *original* message, before */
/*             a reply was generated (so you     */
/*             will need to remember this before */
/*             setting your_ref to my_ref prior  */
/*             to replying);                     */
/*                                               */
/*             The value of the my_ref field     */
/*             filled in by the Wimp after       */
/*             Wimp_SendMessage was called to    */
/*             send the reply.                   */
/*************************************************/

static void protocols_util_update_reference(int old_ref, int new_ref)
{
  /* This would look really great as a switch statement. But hey, */
  /* I'm sure C is really an excellent language. Really.          */

  if      (old_ref == save_source_reference)              save_source_reference            = new_ref;
  else if (old_ref == save_as_text_reference)             save_as_text_reference           = new_ref;
  else if (old_ref == save_as_draw_reference)             save_as_draw_reference           = new_ref;
  else if (old_ref == save_link_reference)                save_link_reference              = new_ref;
  else if (old_ref == save_location_reference)            save_location_reference          = new_ref;

  #ifndef REMOTE_HOTLIST

    else if (old_ref == save_hotlist_entry_reference)     save_hotlist_entry_reference     = new_ref;
    else if (old_ref == save_hotlist_selection_reference) save_hotlist_selection_reference = new_ref;
    else if (old_ref == save_entire_hotlist_reference)    save_entire_hotlist_reference    = new_ref;

  #endif

  else if (old_ref == save_object_reference)              save_object_reference            = new_ref;
  else if (old_ref == save_image_reference)               save_image_reference             = new_ref;
  else if (old_ref == save_history_reference)             save_history_reference           = new_ref;
  else if (old_ref == print_save_reference)               print_save_reference             = new_ref;

  #ifdef TRACE

    else
    {
      erb.errnum = Utils_Error_Custom_Normal;

      sprintf(erb.errmess,
              "Old message reference %x not recognised in protocols_util_update_reference (has a new save method been created, but not added to this function?)",
              old_ref);

      show_error_ret(&erb);
    }

  #endif
}

/*************************************************/
/* protocols_util_make_unique_name()             */
/*                                               */
/* Generates a browser-unique pathname in the    */
/* given buffer, based on Wimp$ScrapDir. This is */
/* hopefully system-unique as the function       */
/* keeps going until it can't find a file under  */
/* the name it has constructed.                  */
/*                                               */
/* Parameters: Pointer to the buffer;            */
/*                                               */
/*             Size of the buffer.               */
/*                                               */
/* Assumes:    Well it won't crash if you give   */
/*             it a NULL buffer pointer or a     */
/*             buffer size of less than 2 bytes, */
/*             but there's not exactly a huge    */
/*             amount of worth in calling the    */
/*             function under those conditions!  */
/*************************************************/

void protocols_util_make_unique_name(char * buffer, int buffer_size)
{
  int    filecount = 0;
  int    type;
  char * leaf;

  if (!buffer || buffer_size < 2) return;

  /* Clear the buffer first */

  memset(buffer, 0, buffer_size);

  /* Write '<Wimp$ScrapDir>.' (or whatever is defined in Save.h) */
  /* to the buffer - +9 is 8 letters for the leafname (see code  */
  /* below) plus 1 for a terminator.                             */

  if (strlen(Save_ScrapPath) + 9 <= buffer_size) strcpy(buffer, Save_ScrapPath);
  else return;

  /* Append with a unique 8 letter leafname */

  leaf = buffer + strlen(buffer);

  do
  {
    sprintf(leaf, "BTF%05x", filecount++);

    /* Can we find the file? */

    type = 1;

    _swix(OS_File,
          _INR(0,1) | _OUT(0),

          17, /* Read catalogue info for object */
          buffer,

          &type);

    /* Keep going until we run out of files (!) or find an unused name */
  }
  while (type != 0 && filecount <= 0xfffff);

  /* Woo - did we run out? */

  if (filecount > 0xfffff)
  {
    #ifdef TRACE

      erb.errnum = Utils_Error_Custom_Normal;

      StrNCpy0(erb.errmess,
               "Blimey! Ran out of filenames in protocols_util_make_unique_name. Going to try to use Save_ScrapFile instead...");

      show_error_ret(&erb);

    #endif

    *buffer = 0;

    if (strlen(Save_ScrapFile) + 1 <= buffer_size) strcpy(buffer, Save_ScrapFile);

    return;
  }

  /* Finished */

  return;
}