/* 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   : Main.c                                 */
/*                                                 */
/* Purpose: To run.                                */
/*                                                 */
/* Author : A.D.Hodgkinson                         */
/*                                                 */
/* History: 12-Nov-96: Created.                    */
/***************************************************/

#include "setjmp.h"
#include "signal.h"

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

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

#include "HTMLLib.h" /* HTML library API, Which will include html2_ext.h, tags.h and struct.h */
#include "URI.h"     /* URI handler API, in URILib:h */

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

#include "toolbox.h"
#include "quit.h"
#include "proginfo.h"
#include "window.h"
#include "menu.h"
#include "saveas.h"
#include "printdbox.h"
#include "gadgets.h"

#include "svcprint.h"
#include "Global.h"
#include "FromROSLib.h"
#include "TBEvents.h" /* Which includes loads of stuff... */
#include "Utils.h"

#include "Browser.h"
#include "ChoiceDefs.h"
#include "CtrlDefs.h"
#include "Encoding.h"
#include "FontManage.h"
#include "Handlers.h"
#include "History.h"
#include "MiscDefs.h"
#include "Mouse.h"
#include "Protocols.h"
#include "Save.h"
#include "URLutils.h"
#include "Windows.h"

/* The following three are defined or undefined locally */

#undef INCLUDE_HEAPGRAPH
#undef INCLUDE_HIERPROF
#undef INCLUDE_MEMCHECK

#ifdef INCLUDE_HEAPGRAPH
  #include "HeapGraph.HeapGraph.h"
#endif

#ifdef INCLUDE_HIERPROF
  #define HierProf_PROFILE
  #include "HierProf:HierProf.h"
#endif

#ifdef INCLUDE_MEMCHECK
  #include "MemCheck:MemCheck.h"
#endif

/* Finally, Main.h itself */

#include "Main.h"

/* Static function prototypes */

static void              initialise_app      (void);

static _kernel_oserror * open_messages_file  (int which);
static void              close_messages_file (void * control);
static void              load_choices        (void);

static void              catch_errors        (int signum);
static void              termination         (void);

/* Make sure the stack starts at a reasonable size to prevent frequent */
/* stack extensions - this is a directive to the C run time system and */
/* can be removed on other systems which have no equivalent.           */

int __root_stack_size = 16384;

// /*************************************************/
// /* attach_event_handlers()                       */
// /*                                               */
// /* Called when an object is autocreated by the   */
// /* toolbox (see initialise_app). Used to get at  */
// /* the ObjectID of things without needing a      */
// /* specifically generated event to deliver it.   */
// /*************************************************/
//
// int attach_event_handlers(int eventcode,ToolboxEvent *event,IdBlock *idb,void *handle)
// {
//   ObjectId temp;
//
//   ToolboxObjectAutoCreatedEvent *c=(ToolboxObjectAutoCreatedEvent *) event;
//
//   /* We extract the relevant object Id by comparing the template name */
//   /* given in the event structure with something we know about and    */
//   /* proceeding as is relevant.                                       */
//
//   if (!strcmp(c->template_name,"Browser"))
//   {
//     main_window_id = idb->self_id;
//     show_centred(main_window_id);
//   }
//   else if (!strcmp(c->template_name,"ButtonBar"))
//   {
//     temp = idb->self_id;
//     {
//       BBox b;
//       b.xmin=0;
//       b.ymin=-144;
//       b.xmax=16384;
//       b.ymax=0;
//       set_corrected_extent(0,temp,&b);
//     }
//   }
//
//   return 1;
// }

/*************************************************/
/* initialise_app()                              */
/*                                               */
/* Initialises application as a Toolbox task.    */
/*************************************************/

static void initialise_app(void)
{
  int module_version;

  show_error(event_initialise(&idb));

//  show_error(event_register_toolbox_handler(-1,
//                                            Toolbox_ObjectAutoCreated,
//                                            attach_event_handlers,
//                                            NULL));

  /* Register fundamental handlers and initialise as a Toolbox task */

  show_error(event_register_message_handler(Wimp_MQuit, handle_messages, NULL));

  show_error(event_register_toolbox_handler(-1, Toolbox_Error, report_toolbox_error, NULL));

  /* Check the Wimp version; for window managers of 3.8 and above, */
  /* declare a minimum version of 3.80 so borderless windows can   */
  /* still have tools. Otherwise, only ask for 3.1.                */

  _swix(Wimp_ReadSysInfo, _IN(0) | _OUT(0), 7, &module_version);

  if (module_version >= 387) nested_wimp = 1;
  else                       nested_wimp = 0;

  show_error(toolbox_initialise(0,
                                nested_wimp ? WIMPMINH : WIMPMINL,
                                messages_list,
                                event_code_list,
                                task_dir,
                                &meb,
                                &idb,
                                &wimp_version,
                                &task_handle,
                                (void *) &sprite_block));

  /* Open the Choices and Controls files */

  cob = NULL;
  chb = NULL;

  show_error(open_messages_file(0));
  show_error(open_messages_file(1));

  /* If either failed, bomb out. Can't raise a custom error as we have no */
  /* messages file to read it from...                                     */

  if (!cob || !chb) exit(EXIT_FAILURE);

  /* If the system variable 'Browse$IssueDesktopCommand' is set to 'yes', */
  /* then the AcornURI and/or TaskModule modules were started in !Run and */
  /* we must issue a *Desktop command to start their task components.     */

  {
    char combuf[96];

    sprintf(combuf, "If \"<Browse$IssueDesktopCommand>\" = \"yes\" Then WimpTask Desktop\r\n");

    _swix(OS_CLI, /* Don't want to hear about any errors */
          _IN(0),

          combuf);
  }

  /* Quit menu items are set to give the Quit_Quit event type, as */
  /* well as this event possibly being delivered by the Toolbox   */
  /* from elsewhere.                                              */

  show_error(event_register_toolbox_handler(-1, Quit_Quit, handle_quit, NULL));

  /* Called before the application's Info box is shown */

  show_error(event_register_toolbox_handler(-1, ProgInfo_AboutToBeShown, handle_show_info, NULL));

  /* Opening and closing windows */

  show_error(event_register_toolbox_handler(-1, EOpenNewWindow, windows_new_browser,  NULL));
  show_error(event_register_toolbox_handler(-1, ECloseWindow,   windows_shut_browser, NULL));

  /* Opening the Open URL dialogue */

  show_error(event_register_toolbox_handler(-1, EOpenToBeShownMisc, openurl_to_be_shown,       NULL));
  show_error(event_register_toolbox_handler(-1, EOpenToBeShownMenu, openurl_to_show_from_menu, NULL));

  /* Called when the Find dialogue is to be shown */

  show_error(event_register_toolbox_handler(-1, EFindToBeShown, find_to_be_shown, NULL));

  /* Print and Print Style dialogues */

  show_error(event_register_toolbox_handler(-1, EPSToBeShown,             printstyle_to_be_shown, NULL));
  show_error(event_register_toolbox_handler(-1, PrintDbox_AboutToBeShown, print_to_be_shown,      NULL));

  /* Called whenever a menu item is selected */

  show_error(event_register_toolbox_handler(-1, Menu_Selection, menus_item_selected, NULL));

  /* Before showing and after closing menus */

  show_error(event_register_toolbox_handler(-1, EUtilsToBeShown,    menus_show_utils,   NULL));
  show_error(event_register_toolbox_handler(-1, EExportToBeShown,   menus_show_export,  NULL));
  show_error(event_register_toolbox_handler(-1, EChoicesToBeShown,  menus_show_choices, NULL));
  show_error(event_register_toolbox_handler(-1, EFileToBeShown,     menus_show_file,    NULL));
  show_error(event_register_toolbox_handler(-1, EDocumentToBeShown, menus_show_main,    NULL));
  show_error(event_register_toolbox_handler(-1, EDocumentHidden,    menus_hide_main,    NULL));
  show_error(event_register_toolbox_handler(-1, EEncodingToBeShown, encoding_show_menu, NULL));

  /* Called when a selection is made from the Encoding menu. */

  show_error(event_register_toolbox_handler(-1, EEncodingSelect, encoding_select, NULL));

  /* Called when the user selects "From document" in the */
  /* Encoding menu.                                      */

  show_error(event_register_toolbox_handler(-1, EEncodingFromDocument, encoding_from_document_select, NULL));

  /* General key press handler */

  show_error(event_register_wimp_handler(-1, Wimp_EKeyPressed, handle_keys, NULL));

  /* Wimp handler for menu selections in forms etc. */

  show_error(event_register_wimp_handler(-1, Wimp_EMenuSelection, handle_menus, NULL));

  /* LoseCaret event handler for grabbing the caret back */

  show_error(event_register_wimp_handler(-1, Wimp_ELoseCaret, handle_lose_caret, NULL));

  /* Pointer checking */

  show_error(event_register_wimp_handler(-1, Wimp_EPointerEnteringWindow, browser_pointer_entering, NULL));
  show_error(event_register_wimp_handler(-1, Wimp_EPointerLeavingWindow,  browser_pointer_leaving,  NULL));

  /* Related to that, end of drag handling */

  show_error(event_register_wimp_handler(-1, Wimp_EUserDrag, (WimpEventHandler *) handle_drags, NULL));

  /* General Wimp message handling */

  show_error(event_register_message_handler(Wimp_MModeChange,   handle_messages, NULL));
  show_error(event_register_message_handler(Wimp_MDataLoad,     handle_messages, NULL));
  show_error(event_register_message_handler(Wimp_MDataLoadAck,  handle_messages, NULL));
  show_error(event_register_message_handler(Wimp_MDataSave,     handle_messages, NULL));
  /* (DataSaveAck is registered in the Printing section below) */
  show_error(event_register_message_handler(Wimp_MDataOpen,     handle_messages, NULL));
  show_error(event_register_message_handler(Wimp_MRAMFetch,     handle_messages, NULL));
  show_error(event_register_message_handler(Wimp_MRAMTransmit,  handle_messages, NULL));
  show_error(event_register_message_handler(Wimp_MMenusDeleted, handle_messages, NULL));

  /* ANT protocols */

  show_error(event_register_message_handler(Message_ANTOpenURL, handle_messages, NULL));

  /* URI handler message handling */

  show_error(event_register_message_handler(URI_MProcess,      handle_messages, NULL));
  show_error(event_register_message_handler(URI_MReturnResult, handle_messages, NULL));

  /* AppControl message handling */

  show_error(event_register_message_handler(Wimp_MAppControl, handle_messages, NULL));

  /* Printing related message handlers */

  show_error(event_register_message_handler(Browser_Message_PrintError,   handle_messages, NULL));
  show_error(event_register_message_handler(Browser_Message_PrintSave,    handle_messages, NULL));
  show_error(event_register_message_handler(Browser_Message_PrintTypeOdd, handle_messages, NULL));

  show_error(event_register_message_handler(Wimp_MDataSaveAck,            handle_messages, NULL));

  /* For message bounces */

  show_error(event_register_wimp_handler(-1, Wimp_EUserMessageAcknowledge, handle_ack, NULL));

  /* Debug build event handlers */

  #ifdef TRACE

    show_error(event_register_toolbox_handler(-1, ETraceTokenDumpByLine,   trace_dump_tokens_by_line,   NULL));
    show_error(event_register_toolbox_handler(-1, ETraceTokenDumpByStream, trace_dump_tokens_by_stream, NULL));

  #endif

  /* Event handlers for menu items that relate to toolbar buttons. */
  /* This list needs to be kept in sync with the specific list in  */
  /* Windows.c, which allows buttons in specific windows to use    */
  /* the same event codes.                                         */

  show_error(event_register_toolbox_handler(-1, EButtonBarHome,          handle_home,           NULL));
  show_error(event_register_toolbox_handler(-1, EButtonBarBack,          handle_back,           NULL));
  show_error(event_register_toolbox_handler(-1, EButtonBarForward,       handle_forwards,       NULL));
  show_error(event_register_toolbox_handler(-1, EButtonBarReload,        handle_reload,         NULL));
  show_error(event_register_toolbox_handler(-1, EButtonBarStop,          handle_stop,           NULL));
  show_error(event_register_toolbox_handler(-1, EButtonBarViewHotlist,   handle_view_hotlist,   NULL));
  show_error(event_register_toolbox_handler(-1, EButtonBarAddToHotlist,  handle_add_hotlist,    NULL));
  show_error(event_register_toolbox_handler(-1, EButtonBarViewResources, handle_view_resources, NULL));
  show_error(event_register_toolbox_handler(-1, EButtonBarLoadImages,    handle_load_images,    NULL));
  show_error(event_register_toolbox_handler(-1, EButtonBarViewSource,    handle_view_source,    NULL));
  show_error(event_register_toolbox_handler(-1, EButtonBarGoTo,          handle_go_to,          NULL));
  show_error(event_register_toolbox_handler(-1, EButtonBarGo,            handle_go,             NULL));
  show_error(event_register_toolbox_handler(-1, EButtonBarCancel,        handle_cancel,         NULL));

  show_error(event_register_toolbox_handler(-1, EButtonBarBistate,       handle_bistate,        NULL));
  show_error(event_register_toolbox_handler(-1, EButtonBarTristate,      handle_tristate,       NULL));

  show_error(event_register_toolbox_handler(-1, EButtonBarSaveSource,    handle_save_src,       NULL));
  show_error(event_register_toolbox_handler(-1, EButtonBarPrint,         handle_print,          NULL));

  /* Miscellaneous event handlers for keyboard control of some functions */

  show_error(event_register_toolbox_handler(-1, EURLBarClearURL,      handle_clear_url,         NULL));
  show_error(event_register_toolbox_handler(-1, EURLBarToggleHistory, handle_show_history_menu, NULL));

  /* Event handlers for saving (most are registered when the dialogue opens) */

  show_error(event_register_toolbox_handler(-1, ESaveFileToBeShown,   savefile_to_be_shown,     NULL));
  show_error(event_register_toolbox_handler(-1, ESaveObjectToBeShown, saveobject_to_be_shown,   NULL));

  /* Wake up flex */

  strncpy(program_name,
          lookup_token("_TaskName", 1, 0),
          sizeof(program_name));

  program_name[sizeof(program_name) - 1] = 0;

  flex_init(program_name, NULL, 0x3000000);
  flex_set_budge(1);
  flex_set_deferred_compaction(1);

  /* Initialise the HTML library. This is to set up some initial */
  /* data which can't be initialised in headers due to ROM build */
  /* considerations.                                             */

  HtmlInit();

  /* Similarly, initialise ImageLib */

  ImageLib_Init();

  /* Initialise FromROSLib routines */

  wimpt_read();

  /* Initialise Utils routines */

  read_os_to_points();

  #ifndef REMOTE_HOTLIST

    /* Initialise the hotlist */

    show_error(hotlist_initialise());
    hotlist_load(lookup_choice("HotlistPath:Browse:User.Hotlist",0,0));

  #endif

  /* Load the history */

  show_error(history_load(lookup_choice("HistoryPath:Browse:User.History",0,0)));

  /* Find the total number of animation frames for the status bar */

  animation_frames = 0;
  {
    char v[10];

    /* SpriteOp 40 is Read Info; it's just something that will give   */
    /* an error if the sprite doesn't exist.                          */

    do
    {
      sprintf(v, "a%d\0", animation_frames ++);
    }
    while (
            animation_frames < Limits_Misc_AnimFrames &&
            !(_swix(OS_SpriteOp,
                    _INR(0,2),

                    296,
                    sprite_block,
                    v))
          );

    /* animation_frames is incremented for every sprite looked at, */
    /* including the last one, which must not be found. So need to */
    /* subtract 1 now, to make it equal the number of frames.      */

    animation_frames --;
  }

  /* Similarly, find the number of bullets available */

  bullets = 0;
  {
    char v[10];

    do
    {
      sprintf(v,"b%d\0",bullets++);
    }
    while (
            bullets < Limits_Misc_Bullets &&
            !(_swix(OS_SpriteOp,
                    _INR(0,2),

                    296,
                    sprite_block,
                    v))
          );

    bullets --;
  }

  /* Is the URI handler available? */

  {
    int version;

    if (uri_version(0, &version)) uri_module_present = 0;
    else if (version >= 5)        uri_module_present = 1;
  }

  /* Find out window tool sizes */

  ChkError(windows_initialise_tool_sizes());
}

/*************************************************/
/* open_messages_file()                          */
/*                                               */
/* Asks MessageTrans to open a Messages file.    */
/* Looks through a system variable for the path  */
/* to find the file in before going to a         */
/* default - see the code comments for more      */
/* information.                                  */
/*                                               */
/* Parameters: 0 to load the Choices file, or 1  */
/*             to load the Controls file.        */
/*************************************************/

static _kernel_oserror * open_messages_file(int which)
{
  _kernel_oserror * e;
  MessagesFD      * control;
  char            * path = NULL;

  /* Work out what to open. */

  path = save_build_messages_path(which);

  if (!path) goto open_messages_file_no_memory;

  /* Claim RMA for the control block and pathname */

  e = _swix(OS_Module,
            _IN(0) | _IN(3) | _OUT(2),

            6,
            sizeof(MessagesFD) + strlen(path) + 1,

            &control);

  if (e)
  {
    free(path);

    return e;
  }

  /* Update the relevant global. No need for a default case as */
  /* save_build_messages_path will have caught that.           */

  switch (which)
  {
    case 0: chb = control; break;
    case 1: cob = control; break;
  }

  /* Register the block with MemCheck if required, and copy */
  /* the pathname into it.                                  */

  #ifdef INCLUDE_MEMCHECK
    MemCheck_RegisterMiscBlock((void *) control, sizeof(MessagesFD) + strlen(path) + 1);
  #endif

  strcpy((char *) ((int) control + sizeof(MessagesFD)), path);

  /* Don't need the path now */

  free(path);
  path = NULL;

  /* Open the file */

  return _swix(MessageTrans_OpenFile,
               _INR(0,2),

               control,                            /* Pointer to control block */
               (int) control + sizeof(MessagesFD), /* Filename                 */
               0);                                 /* Buffer in RMA            */

  /* Error condition exit */

open_messages_file_no_memory:

  if (control) _swix(OS_Module,
                     _IN(0) | _IN(2),

                     7,
                     control);

  if (path) free(path);

  return NULL;
}

/*************************************************/
/* close_messages_file()                         */
/*                                               */
/* Closes a messges file and releases the RMA    */
/* space claimed for it.                         */
/*                                               */
/* Parameters: Pointer to the allocated chunk of */
/*             RMA space holding the             */
/*             MessageTrans control block and    */
/*             Messages file pathname.           */
/*************************************************/

static void close_messages_file(void * control)
{
  /* If the file won't close for some reason, */
  /* MessageTrans may still want to access it */
  /* - so safest *not* to release the RMA     */
  /* holding the control block and filename.  */

  if (
       _swix(MessageTrans_CloseFile,
             _IN(0),

             control)
     )
     return;

  /* Release the claimed RMA space holding the */
  /* control block and messages file name.     */

  _swix(OS_Module,
        _IN(0) | _IN(2),

        7,
        control);
}

/*************************************************/
/* encoding_leaf_to_path()                       */
/*                                               */
/* Called by the encoder in HTMLLib. Print a     */
/* full encoding file pathname into path given   */
/* the leafname.                                 */
/*                                               */
/* Parameters: Pointer to output buffer for full */
/*             pathname;                         */
/*                                               */
/*             Pointer to leaf name.             */
/*                                               */
/* Assumes:    Output buffer big enough :)       */
/*************************************************/

void encoding_leaf_to_path(char *path, const char *leaf)
{
  sprintf(path, "%s.Encodings.%s", task_dir, leaf);
}

/*************************************************/
/* load_choices()                                */
/*                                               */
/* Reads in the choices from the Messages file,  */
/* filling in the global_choices structure,      */
/* 'choices' (see Global.c and Global.h).        */
/*************************************************/

static void load_choices(void)
{
  /* First, the user-configurable options from the Choices file. */

  /* Set the various default colours */

  choices.background_colour = (unsigned int) strtoul(lookup_choice("BackColour:0xdddddd00", 0, 0), NULL, 16);
  choices.text_colour       = (unsigned int) strtoul(lookup_choice("TextColour:0x00000000", 0, 0), NULL, 16);
  choices.link_colour       = (unsigned int) strtoul(lookup_choice("LinkColour:0xff000000", 0, 0), NULL, 16);
  choices.used_colour       = (unsigned int) strtoul(lookup_choice("UsedColour:0xbb008800", 0, 0), NULL, 16);
  choices.followed_colour   = (unsigned int) strtoul(lookup_choice("FollColour:0x0000ff00", 0, 0), NULL, 16);
  choices.selected_colour   = (unsigned int) strtoul(lookup_choice("SeleColour:0x00bb0000", 0, 0), NULL, 16);

  /* Font usage */

  choices.font_size = atoi(lookup_choice("FontSize:12", 0, 0));

  if (choices.font_size < 6  * 16) choices.font_size = 6;
  if (choices.font_size > 24 * 16) choices.font_size = 24;

  /* Wake up the font library */

  fm_init(choices.system_font, choices.font_size);

  show_error(fm_define_default_typefaces());

  fm_claim_basic_typefaces(choices.font_size);

  /* Look up the typeface definitions */

  {
    int  face;
    char tokenname[12];

    /* Arbitrary limit... Typeface1 to Typeface99 */

    for (face = 1; face < 100; face ++)
    {
      sprintf(tokenname, "Typeface%d", face);

      /* Look up the token, exit if it fails */

      lookup_choice(tokenname, 0, 0);
      if (*tokens == '!') break;

      /* Otherwise, define the typeface */

      show_error(fm_define_typeface(tokens));
    }
  }

  choices.encoding  = atoi(lookup_choice("Encoding:4",  0, 0));

  /* Force system font if we're using UTF-8 */

  #ifdef UNIFONT

    choices.system_font = 1;

  #else

    if (!strcmp(lookup_choice("SystemFont:no", 0, 0),"yes")) choices.system_font = 1;

  #endif

  if (choices.system_font) choices.font_size = FM_Standard_Size;

  /* Page display */

  if (!strcmp(lookup_choice("UnderlineLinks:yes", 0, 0), "yes")) choices.underline_links = 1;
  if (!strcmp(lookup_choice("UseSourceCols:yes",  0, 0), "yes")) choices.use_source_cols = 1;
  if (!strcmp(lookup_choice("ShowForeground:yes", 0, 0), "yes")) choices.show_foreground = 1;
  if (!strcmp(lookup_choice("ShowBackground:yes", 0, 0), "yes")) choices.show_background = 1;

  /* Page layout */

  choices.left_margin  = atoi(lookup_choice("LeftMargin:1600",   0, 0));
  choices.right_margin = atoi(lookup_choice("RightMargin:6400",  0, 0));
  choices.quote_margin = atoi(lookup_choice("QuoteMargin:19200", 0, 0));
  choices.leading      = atoi(lookup_choice("Leading:4",         0, 0));
  choices.left_indent  = atoi(lookup_choice("LeftIndent:12800",  0, 0));

  /* Fetch controls */

  /* Limit the number of simultaneous image fetches (I recommend */
  /* a minimum of 2, rather than 1, as most efficient).          */

  choices.maximages = atoi(lookup_choice("MaxImages:5", 0, 0));
  if (choices.maximages <= 0) choices.maximages = 1;

  if (!strcmp(lookup_choice("ClientPull:yes", 0, 0), "yes")) choices.client_pull = 1;

  /* Hotlist controls */

  if      (!strcmp(lookup_choice("SaveHotlist", 0, 0), "never"))  choices.save_hotlist = Choices_SaveHotlist_Never;
  else if (!strcmp(lookup_choice("SaveHotlist", 0, 0), "always")) choices.save_hotlist = Choices_SaveHotlist_Always;
  else                                                            choices.save_hotlist = Choices_SaveHotlist_Once;

  if (!strcmp(lookup_choice("AddHotlist", 0, 0), "bottom")) choices.add_hotlist  = Choices_AddHotlist_Bottom;
  else                                                      choices.add_hotlist  = Choices_AddHotlist_Top;

  if (!strcmp(lookup_choice("HotlistType", 0, 0), "urls"))  choices.hotlist_show = Choices_HotlistType_URLs;
  else                                                      choices.hotlist_show = Choices_HotlistType_Descriptions;

  choices.auto_open_delay = atoi(lookup_choice("AutoOpenDelay:100", 0, 0));
  if (choices.auto_open_delay > 1000) choices.auto_open_delay = 1000;
  if (choices.auto_open_delay < 0)    choices.auto_open_delay = 0;

  choices.auto_scroll_delay = atoi(lookup_choice("AutoScrollDelay:50", 0, 0));
  if (choices.auto_scroll_delay > 1000) choices.auto_scroll_delay = 1000;
  if (choices.auto_scroll_delay < 0)    choices.auto_scroll_delay = 0;

  choices.auto_scroll_margin = atoi(lookup_choice("AutoScrollMargin:48", 0, 0));
  if (choices.auto_scroll_margin > 256) choices.auto_scroll_margin = 256;
  if (choices.auto_scroll_margin < 0)   choices.auto_scroll_margin = 0;

  /* Global and local histories */

  choices.g_hist_size = atoi(lookup_choice("GHistSize:8",  0, 0));
  choices.v_hist_size = atoi(lookup_choice("VHistSize:50", 0, 0));

  if (choices.g_hist_size < 1) choices.g_hist_size = 1;
  if (choices.v_hist_size < 4) choices.v_hist_size = 4;

  if (!strcmp(lookup_choice("ShowURLs:no", 0, 0), "yes")) choices.show_urls = 1;

  if      (!strcmp(lookup_choice("SaveHistory", 0, 0), "never"))  choices.save_history = Choices_SaveHistory_Never;
  else if (!strcmp(lookup_choice("SaveHistory", 0, 0), "always")) choices.save_history = Choices_SaveHistory_Always;
  else                                                            choices.save_history = Choices_SaveHistory_Once;

  /* Toolbar controls */

  if (!strcmp(lookup_choice("URLbar:yes",    0, 0), "yes")) choices.url_bar    = 1;
  if (!strcmp(lookup_choice("ButtonBar:yes", 0, 0), "yes")) choices.button_bar = 1;
  if (!strcmp(lookup_choice("StatusBar:yes", 0, 0), "yes")) choices.status_bar = 1;

  if      (!strcmp(lookup_choice("MoveGadgets", 0, 0), "never"))  choices.move_gadgets = Choices_MoveGadgets_Never;
  else if (!strcmp(lookup_choice("MoveGadgets", 0, 0), "at end")) choices.move_gadgets = Choices_MoveGadgets_AtEnd;
  else                                                            choices.move_gadgets = Choices_MoveGadgets_During;

  /* Window controls */

  choices.width      = atoi(lookup_choice("Width:1024",  0, 0));
  choices.height     = atoi(lookup_choice("Height:1280", 0, 0));
  choices.override_x = atoi(lookup_choice("OverrideX:0",    0, 0));
  choices.override_y = atoi(lookup_choice("OverrideY:0",    0, 0));

  if      (!strcmp(lookup_choice("SolidResize", 0, 0), "no"))     choices.solid_resize = Choices_SolidResize_No;
  else if (!strcmp(lookup_choice("SolidResize", 0, 0), "always")) choices.solid_resize = Choices_SolidResize_Always;
  else                                                            choices.solid_resize = Choices_SolidResize_Yes;

  if (!strcmp(lookup_choice("FullScreen:no", 0, 0), "yes")) choices.full_screen = 1;

  if      (!strcmp(lookup_choice("HScroll", 0, 0), "no"))  choices.h_scroll = Choices_HScroll_No;
  else if (!strcmp(lookup_choice("HScroll", 0, 0), "yes")) choices.h_scroll = Choices_HScroll_Yes;
  else                                                     choices.h_scroll = Choices_HScroll_Auto;

  if      (!strcmp(lookup_choice("VScroll", 0, 0), "no"))  choices.v_scroll = Choices_HScroll_No;
  else if (!strcmp(lookup_choice("VScroll", 0, 0), "yes")) choices.v_scroll = Choices_HScroll_Yes;
  else                                                     choices.v_scroll = Choices_HScroll_Auto;

  /* Reformatter controls */

  if (!strcmp(lookup_choice("RefoWait:no", 0, 0), "yes")) choices.refo_wait = 1;
  if (!strcmp(lookup_choice("RefoHang:no", 0, 0), "yes")) choices.refo_hang = 1;

  choices.refo_time = atoi(lookup_choice("RefoTime:500", 0, 0));
  if (choices.refo_time < 25)   choices.refo_time = 25;
  if (choices.refo_time > 2000) choices.refo_time = 2000;

  /* Input device controls */

  if (!strcmp(lookup_choice("FixedPtr:yes",    0, 0), "yes")) choices.fixed_pointer     = 1;
  if (!strcmp(lookup_choice("HighlightLks:no", 0, 0), "yes")) choices.highlight_links = 1;
  if (!strcmp(lookup_choice("KeyboardCtl:no",  0, 0), "yes")) choices.keyboard_ctrl  = 1;

  /* Multiuser environments and proxying */

  if (!strcmp(lookup_choice("UseProxy:no", 0, 0), "yes")) choices.use_proxy = 1;
  if (!strcmp(lookup_choice("Clone:yes",   0, 0), "yes")) choices.clone     = 1;

  /* If compiling for a multiuser environment, these will */
  /* exist and need setting to null strings initially.    */

  #ifndef SINGLE_USER

    choices.username[0] = 0;
    choices.password[0] = 0;

  #endif

  /* Non user-configurable options from the Controls file */

  /* Animation controls */

  controls.anim_delay = atoi(lookup_control("AnimSpeed:4", 0, 0));

  if (!strcmp(lookup_control("AnimDrift:no", 0, 0), "yes")) controls.anim_drift = 1;
  if (!strcmp(lookup_control("DBoxAnims:no", 0, 0), "yes")) controls.dbox_anims = 1;

  /* Main window and general toolbar controls */

  controls.minimum_convergence = atoi(lookup_control("MinConvergence:480", 0, 0));

  if      (!strcmp(lookup_control("DontGrey", 0, 0), "none"))    controls.dont_grey = Controls_DontGrey_GreyNone;
  else if (!strcmp(lookup_control("DontGrey", 0, 0), "history")) controls.dont_grey = Controls_DontGrey_GreyHistoryOnly;
  else                                                           controls.dont_grey = Controls_DontGrey_GreyAll;

  if (!strcmp(lookup_control("SwapBars:no",   0, 0), "yes")) controls.swap_bars   = 1;
  if (!strcmp(lookup_control("BackWindow:no", 0, 0), "yes")) controls.back_window = 1;
  if (!strcmp(lookup_control("UseSmall:yes",  0, 0), "yes")) controls.use_small   = 1;

  /* Main and dialler status controls */

  if (!strcmp(lookup_control("ClaimHelp:no", 0, 0), "yes")) controls.claim_help = 1;

  controls.show_help_for         = atoi(lookup_control("ShowHelpFor:600",   0, 0));
  controls.show_dstat_for        = atoi(lookup_control("ShowDStatFor:300",  0, 0));
  controls.show_links_for        = atoi(lookup_control("ShowLinksFor:200",  0, 0));
  controls.show_misc_for         = atoi(lookup_control("ShowMiscFor:50",    0, 0));
  controls.quantise              = atoi(lookup_control("Quantise:5",        0, 0));
  controls.progress_update_delay = atoi(lookup_control("ProgressDelay:50",  0, 0));

  /* Progress indicator controls */

  if (!strcmp(lookup_control("AppendStatus:no", 0, 0), "yes")) controls.append_status = 1;
  if (!strcmp(lookup_control("UseBrackets:yes", 0, 0), "yes")) controls.use_brackets  = 1;

  /* The ColourProgress option in Controls is a little unusual; it holds */
  /* 'no' (NotAColour, see CtrlDefs.h) or a Wimp colour number. Default  */
  /* to 11 (red, in the standard Wimp palette).                          */

  if (!strcmp(lookup_control("ColourProgress:11", 0, 0), "no")) controls.colour_progress = Controls_ColourProgress_NotAColour;
  else
  {
    controls.colour_progress = atoi(lookup_control("ColourProgress:11", 0, 0));
    if (controls.colour_progress > 15) controls.colour_progress = 11;
  }

  /* Frame controls */

  controls.minimum_frame_height = atoi(lookup_control("MinFrmHeight:48", 0, 0));
  controls.minimum_frame_width  = atoi(lookup_control("MinFrmWidth:48",  0, 0));

  if (!strcmp(lookup_control("KeepHighlight:no", 0, 0), "yes")) controls.keep_highlight = 1;

  /* Input device controls */

  if (!strcmp(lookup_control("KeepCaret:no",    0, 0), "yes")) controls.keep_caret    = 1;
  if (!strcmp(lookup_control("ClearFirst:yes",  0, 0), "yes")) controls.clear_first   = 1;
  if (!strcmp(lookup_control("LockToLine:no",   0, 0), "yes")) controls.lock_to_line  = 1;
  if (!strcmp(lookup_control("IgnoreAdjust:no", 0, 0), "yes")) controls.ignore_adjust = 1;

  /* Remote hotlist support */

  if (!strcmp(lookup_control("AppendURLs:no", 0, 0), "yes")) controls.append_urls = 1;

  /* Fetch controls */

  if (!strcmp(lookup_control("BrickWall:no",   0, 0), "yes")) controls.brick_wall    = 1;
  if (!strcmp(lookup_control("StopWebServ:no", 0, 0), "yes")) controls.stop_webserve = 1;

  controls.back_off_at = atoi(lookup_control("BackOffAt:128", 0, 0));

  /* Mouse pointer active point offsets */

  {
    int offset;

    offset = atoi(lookup_control("PtrLnkActvX:5", 0,0)); controls.ptrlnkactvx  = (char) offset;
    offset = atoi(lookup_control("PtrLnkActvY:1", 0,0)); controls.ptrlnkactvy  = (char) offset;
    offset = atoi(lookup_control("PtrMapActvX:7", 0,0)); controls.ptrmapactvx  = (char) offset;
    offset = atoi(lookup_control("PtrMapActvY:7", 0,0)); controls.ptrmapactvy  = (char) offset;
    offset = atoi(lookup_control("PtrUDActvX:5",  0,0)); controls.ptrudactvx   = (char) offset;
    offset = atoi(lookup_control("PtrUDActvY:8",  0,0)); controls.ptrudactvy   = (char) offset;
    offset = atoi(lookup_control("PtrLRActvX:8",  0,0)); controls.ptrlractvx   = (char) offset;
    offset = atoi(lookup_control("PtrLRActvY:5",  0,0)); controls.ptrlractvy   = (char) offset;
    offset = atoi(lookup_control("PtrUDLRActvX:8",0,0)); controls.ptrudlractvx = (char) offset;
    offset = atoi(lookup_control("PtrUDLRActvY:5",0,0)); controls.ptrudlractvy = (char) offset;
    offset = atoi(lookup_control("PtrNoRActvX:7", 0,0)); controls.ptrnoractvx  = (char) offset;
    offset = atoi(lookup_control("PtrNoRActvY:7", 0,0)); controls.ptrnoractvy  = (char) offset;
    offset = atoi(lookup_control("PtrToSActvX:0", 0,0)); controls.ptrtosactvx  = (char) offset;
    offset = atoi(lookup_control("PtrToSActvY:0", 0,0)); controls.ptrtosactvy  = (char) offset;
    offset = atoi(lookup_control("PtrScrActvX:8", 0,0)); controls.ptrscractvx  = (char) offset;
    offset = atoi(lookup_control("PtrScrActvY:8", 0,0)); controls.ptrscractvy  = (char) offset;
  }

  #ifdef TRACE
    /* This list is somewhat out of date...! Still, it served */
    /* its purpose in the early days of the above routines,   */
    /* and gives a useful overview for the Rout debug option  */
    /* during startup.                                        */

    if (tl & (1u<<5))
    {
      Printf("\nWidth: %d\n"
               "Height: %d\n",
                choices.width,
                choices.height);

      Printf("\nBack colour: %p\n"
               "Text colour: %p\n"
               "Link colour: %p\n"
               "Used colour: %p\n",
                (void *) choices.background_colour,
                (void *) choices.text_colour,
                (void *) choices.link_colour,
                (void *) choices.used_colour);

      Printf("\nSystem font: %d\n"
               "Show foreground images: %d\n"
               "Show background images: %d\n"
               "Fixed pointer: %d\n"
               "Underline links: %d\n"
               "Use document colours: %d\n"
               "URL bar: %d\n"
               "Button bar: %d\n"
               "Status bar: %d\n"
               "Move gadgets: %d\n"
               "Use a proxy: %d\n\n",
                choices.system_font,
                choices.show_foreground,
                choices.show_background,
                choices.fixed_pointer,
                choices.underline_links,
                choices.use_source_cols,
                choices.url_bar,
                choices.button_bar,
                choices.status_bar,
                choices.move_gadgets,
                choices.use_proxy);
    }
  #endif

  /* Install any general handlers that might be needed as a */
  /* result of the choices just loaded.                     */

  show_error(event_register_wimp_handler(-1, Wimp_ELoseCaret, handle_lose_caret, NULL));

  if (controls.claim_help)
  {
    /* Interactive help support for showing help in the status bar */

    register_null_claimant(Wimp_ENull, protocols_ih_send_help_request, NULL);
    show_error(event_register_message_handler(Wimp_MHelpReply, handle_messages, NULL));
  }
}

/*************************************************/
/* catch_errors()                                */
/*                                               */
/* Catch OS errors and report them with the      */
/* opportunity to continue or quit.              */
/*                                               */
/* Parameters: The signal number (ignored).      */
/*************************************************/

static void catch_errors(int signum)
{
  _kernel_oserror * e = _kernel_last_oserror();

  if (e) erb = *e;

  print_abort_print();

  if (e) show_error_cont(&erb);

  exit(EXIT_FAILURE);
}

/*************************************************/
/* termination()                                 */
/*                                               */
/* Called by registration through the atexit     */
/* function. Shuts down core functions prior to  */
/* the browser exitting (e.g. the Font Manager   */
/* can get very tetchy about having font handles */
/* left claimed, so must release them).          */
/*************************************************/

void termination(void)
{
  #ifdef TRACE
    if (tl & (1u<<5)) Printf("termination() called\n");
  #endif

  if (taskmodule_ds_registered)
  {
    /* Not interested in any errors, if it fails we can't really */
    /* do anything about it at this stage.                       */

    _swix(TaskModule_DeRegisterService,
          _INR(0,2),

          0,
          0,
          task_handle);
  }

  close_messages_file(cob); cob = NULL;
  close_messages_file(chb); chb = NULL;

  fm_shutdown();
}

/*************************************************/
/* main()                                        */
/*                                               */
/* That which runs before all others.            */
/*************************************************/

int main(int argc, char * argv[])
{
  WimpPollBlock b;
  int           eventcode, time;
  int           argp = 1, done_one = 0;

  #ifdef INCLUDE_HEAPGRAPH
    HeapGraph_RedirectAllocFns(NULL);
  #endif

  #ifdef INCLUDE_HIERPROF
    HierProf_ProfileAllFunctions();
  #endif

  #ifdef INCLUDE_MEMCHECK
    MemCheck_Init();
    MemCheck_InterceptSCLStringFunctions();
    MemCheck_RegisterArgs(argc, argv);
    MemCheck_SetStoreMallocFunctions(1);
    MemCheck_SetReportFrees(1);
    MemCheck_SetAutoOutputBlocksInfo(0);
  #endif

  #ifndef TRACE

    /* Prevent postmortems; instead, report errors in a standard window. */
    /* If backtraces are needed, use the debugger...                     */
    /*                                                                   */
    /* If compiling a version for the debugger, ensure TRACE is defined  */
    /* or the code is commented out. The code below can mess up the      */
    /* debugger as default handlers are not beng used, and trying to     */
    /* install them tramples on the debugger's environment setup.        */

    {
      int loop, ret1, ret2, ret3;

      /* Loop over the undefined instruction, prefetch abort, data abort */
      /* and address exception handlers, resetting them to the RISC OS   */
      /* defaults. The RISC OS default handlers raise a standard RISC OS */
      /* error, having filled in the *ShowRegs register dump. (See PRM   */
      /* volume 1 page 315 onwards).                                     */

      for (loop = 1; loop <= 4; loop ++)
      {
        _swix(OS_ReadDefaultHandler,
              _IN(0) | _OUTR(1,3),

              loop,
              &ret1,
              &ret2,
              &ret3);

        _swix(OS_ChangeEnvironment,
              _INR(0,3),

              loop,
              ret1,
              ret2,
              ret3);
      }
    }

  #endif

  signal(SIGOSERROR, catch_errors); /* OS error */
  signal(SIGILL,     catch_errors); /* Illegal instruction */
  signal(SIGSEGV,    catch_errors); /* Segment violation */
  signal(SIGSTAK,    catch_errors); /* Stack overflow */
  signal(SIGFPE,     catch_errors); /* FPE error */

  /* Before initialisation, find out where we ran from - this */
  /* software can support different application names, so the */
  /* existance of a specific system variable cannot be relied */
  /* upon (with the exception of ROM builds).                 */

  {
    int    len;
    char * item;

    /* Work out what path to go through */

    #ifdef ROM

      item = "Resources:$.Resources.Browse";
      len  = strlen(item);

    #else

      item = argv[0];
      len  = strlen(item) - strlen(".!RunImage");

    #endif

    /* Allocate the space, bomb out if it fails */

    task_dir = malloc(len + 1);

    if (!task_dir)
    {
      erb.errnum = Utils_Error_Custom_Fatal;
      strcpy(erb.errmess, "There is insufficient memory to start the browser.");
      show_error(&erb);
    }

    /* Copy the information and ensure it is terminated correctly */

    strncpy(task_dir, item, len);
    task_dir[len] = 0;
  }

  #ifdef TRACE

    malloccount = flexcount = 0;

    /* Handle -d[ebug] CLI switch; see Global.c for more information. */
    /* This must be the first command line argument.                  */

    if (argc >= argp + 1)
    {
      if (!strcmp(argv[argp],"-debug") | !strcmp(argv[argp],"-d"))
      {
        if (strstr(argv[argp + 1], "MsgT")) tl |= (1u<<0);
        if (strstr(argv[argp + 1], "TBar")) tl |= (1u<<1);
        if (strstr(argv[argp + 1], "Null")) tl |= (1u<<2);
        if (strstr(argv[argp + 1], "Wind")) tl |= (1u<<3);
        if (strstr(argv[argp + 1], "Menu")) tl |= (1u<<4);
        if (strstr(argv[argp + 1], "Rout")) tl |= (1u<<5);
        if (strstr(argv[argp + 1], "Fetc")) tl |= (1u<<6);
        if (strstr(argv[argp + 1], "Memo")) tl |= (1u<<7);
        if (strstr(argv[argp + 1], "Refo")) tl |= (1u<<8);
        if (strstr(argv[argp + 1], "Redr")) tl |= (1u<<9);
        if (strstr(argv[argp + 1], "Font")) tl |= (1u<<10);
        if (strstr(argv[argp + 1], "BBox")) tl |= (1u<<11);
        if (strstr(argv[argp + 1], "LMem")) tl |= (1u<<12);
        if (strstr(argv[argp + 1], "CMal")) tl |= (1u<<13);
        if (strstr(argv[argp + 1], "CFle")) tl |= (1u<<14);
        if (strstr(argv[argp + 1], "Imag")) tl |= (1u<<15);
        if (strstr(argv[argp + 1], "Hist")) tl |= (1u<<16);
        if (strstr(argv[argp + 1], "Fram")) tl |= (1u<<17);
        if (strstr(argv[argp + 1], "Stre")) tl |= (1u<<18);
        if (strstr(argv[argp + 1], "Circ")) tl |= (1u<<19);
        if (strstr(argv[argp + 1], "Tabl")) tl |= (1u<<20);
        if (strstr(argv[argp + 1], "URIH")) tl |= (1u<<21);
        if (strstr(argv[argp + 1], "KeyC")) tl |= (1u<<22);
        if (strstr(argv[argp + 1], "RBox")) tl |= (1u<<23);
        if (strstr(argv[argp + 1], "JScr")) tl |= (1u<<24);
        if (strstr(argv[argp + 1], "Hotl")) tl |= (1u<<25);
        if (strstr(argv[argp + 1], "Save")) tl |= (1u<<26);
        if (strstr(argv[argp + 1], "Drag")) tl |= (1u<<27);
        if (strstr(argv[argp + 1], "MsgP")) tl |= (1u<<28);

        if (strstr(argv[argp + 1], "All"))  tl  = 0xffffffff;

        argp += 2;
      }
    }

    if (tl & (1u<<5)) Printf("\nmain: Initialising\n");

  #endif

  /* Now do the bulk of application initialisation */

  initialise_app();

  #ifdef TRACE
    if (tl & (1u<<5)) Printf("main: Loading choices\n");
  #endif

  load_choices();

  #ifdef TRACE
    if (tl & (1u<<5)) Printf("main: Handling CLI arguments\n");
  #endif

  /* If using keyboard control, watch the pointer for movement, */
  /* turning it off if not moved for 5 seconds.                 */

  if (choices.keyboard_ctrl) mouse_watch_pointer_control(1);

  /* Keep advancing argp if the arguments are dealt with;  */
  /* only continue to check the arguments if we haven't    */
  /* pushed argp past argc, the total number of arguments. */

  done_one = 1;

  while (argc >= argp && done_one)
  {
    done_one = 0;

    /* Handle -html (HTML files) */

    if (argc >= argp + 1)
    {
      if (!strcmp(argv[argp], "-html"))
      {
        char url[Limits_URL];

        #ifdef TRACE
          if (tl & (1u<<5)) Printf("main: Handling -html CLI argument\n");
        #endif

        StrNCpy0(url, argv[argp + 1]);
        urlutils_pathname_to_url(url, sizeof(url));

        windows_create_browser(url, NULL, NULL, NULL, 0);

        argp += 2, done_one = 1;
      }
    }

    /* Handle -uri (URI files) */

    if (argc >= argp + 1)
    {
      if (!strcmp(argv[argp], "-uri"))
      {
        char url[Limits_URL];

        #ifdef TRACE
          if (tl & (1u<<5)) Printf("main: Handling -uri CLI argument\n");
        #endif

        urlutils_load_uri_file(url, sizeof(url), argv[argp + 1]);

        windows_create_browser(url, NULL, NULL, NULL, 0);

        argp += 2, done_one = 1;
      }
    }

    /* Handle -url (URL strings) */

    if (argc >= argp + 1)
    {
      if (!strcmp(argv[argp],"-url") || !strcmp(argv[argp],"-u"))
      {
        #ifdef TRACE
          if (tl & (1u<<5)) Printf("main: Handling -url CLI argument\n");
        #endif

        windows_create_browser(argv[argp+1], NULL, NULL, NULL, 0);

        argp += 2, done_one = 1;
      }
    }
  }

  #ifdef TRACE
    if (tl & (1u<<5)) Printf("main: Polling\n");
  #endif

  atexit(termination);

  setjmp(env);

  while (!quit)
  {
    /* We use flex's deferred compaction, so ensure the */
    /* heap is as small as possible.                    */

    flex_compact();

    /* What time is it? (For Wimp_PollIdle) */

    _swix(OS_ReadMonotonicTime,
          _OUT(0),

          &time);

    /* Use PollIdle, but want drag events to be as responsive as possible */

    ChkError(event_poll_idle(&eventcode,
                             &b,
                             time + !drag_in_progress,
                             NULL));
  }

  #ifdef TRACE
    if (tl & (1u<<5))   Printf("\nmain: Calling exit()\n\n");
    if (tl & (1u<<13))  Printf("Near exit, malloccount: \0216%d\0217\n",malloccount);
    if (tl & (1u<<14))  Printf("Near exit, flexcount  : %d\n",flexcount);
  #endif

  #ifndef REMOTE_HOTLIST

    /* Save the hotlist */

    #ifdef TRACE
      if (tl & (1u<<5)) Printf("main: Calling hotlist_save\n");
    #endif

    if (choices.save_hotlist != Choices_SaveHotlist_Never) show_error_ret(hotlist_save(lookup_choice("HotlistPath:Browse:User.Hotlist",0,0)));

  #endif

  /* Save the global history */

  #ifdef TRACE
    if (tl & (1u<<5)) Printf("main: Calling history_save\n");
  #endif

  if (choices.save_history != Choices_SaveHistory_Never) history_save(lookup_choice("HistoryPath:Browse:User.History",0,0));

  /* This will call the termination() function in passing */

  exit(EXIT_SUCCESS);
}