/* 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 "FontManage.h"
#include "Handlers.h"
#include "History.h"
#include "MiscDefs.h"
#include "Mouse.h"
#include "Protocols.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;

/* To hold where the task is coming from (malloced in main) */

static char * task_dir = NULL;

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

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

  /* Register a handler for Toolbox errors */

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

  /* Open the Choices and Controls files */

  cob = chb = NULL;

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

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

  /* Called when the icon bar is clicked upon with Select */

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

  /* Called to close the browser window */

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

  /* Called when the Open URL dialogue is to be shown */

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

  /* Called when the Open URL dialogue is to be shown from a menu */

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

  /* Called when the Print Style dialogue is to be shown */

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

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

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

  /* Called when the Utils submenu is to be opened from the */
  /* main menu in a browser window.                         */

  show_error(event_register_toolbox_handler(-1,
                                            EUtilsToBeShown,
                                            menus_show_utils,
                                            NULL));

  /* Called when the Export submenu is to be opened */

  show_error(event_register_toolbox_handler(-1,
                                            EExportToBeShown,
                                            menus_show_export,
                                            NULL));

  /* Called when the Choices submenu is to be opened */
  /* from the icon bar menu                          */

  show_error(event_register_toolbox_handler(-1,
                                            EChoicesToBeShown,
                                            menus_show_choices,
                                            NULL));

  /* Called when the File submenu is to be opened from */
  /* a browser window's main menu                      */

  show_error(event_register_toolbox_handler(-1,
                                            EFileToBeShown,
                                            menus_show_file,
                                            NULL));

  /* Called whenever the main menu is to be opened */
  /* from a browser window, or closed.             */

  show_error(event_register_toolbox_handler(-1,
                                            EDocumentToBeShown,
                                            menus_show_main,
                                            NULL));

  show_error(event_register_toolbox_handler(-1,
                                            EDocumentHidden,
                                            menus_hide_main,
                                            NULL));

  /* Called whenever a menu item is selected */

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

  /* General key press handler */

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

  /* Menu handler for forms etc. */

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

  /* LoseCaret event handler for grabbing the caret back, etc. */

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

  /* 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);
  flex_set_budge(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;
  }

  /* Try and set the user agent string for HTTP fetches. */

  show_error(utils_set_http_agent());

  /* 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            * sysvar;
  char            * defpath;
  char            * path = NULL;
  int               len, exists;

  /* Work out what to open.                     */
  /*                                            */
  /* If you extend this switch statement, don't */
  /* forget to extend the one later in the      */
  /* function too...                            */

  switch (which)
  {
    case 0:
    {
      sysvar  = "Browse$ChoicesFile";
      defpath = ".Choices";
    }
    break;

    case 1:
    {
      sysvar  = "Browse$ControlsFile";
      defpath = ".Controls";
    }
    break;

    default:
    {
      #ifdef TRACE

        erb.errnum = Utils_Error_Custom_Fatal;

        sprintf(erb.errmess,
                "In open_choices_file, passed unrecognised parameter value '%d'",
                which);

        return &erb;

      #else

        return NULL;

      #endif
    }
  }

  /* Equivalent to getenv(), but the RISC OS implementation evaluates */
  /* the system variable as an expression which we don't want; hence  */
  /* the direct use of the SWI.                                       */

  /* First, find the system variable length. Must use _kernel_swi */
  /* here for various reasons.                                    */

  {
    _kernel_swi_regs r;

    r.r[0] = (int) sysvar;
    r.r[1] = (int) NULL;
    r.r[2] = -1;
    r.r[3] = 0;
    r.r[4] = 0;

    _kernel_swi(OS_ReadVarVal, &r, &r);

    len = -r.r[2]; /* This includes a terminator */
  }

  if (!len)
  {
    /* Variable doesn't exist */

    len    = strlen(task_dir) + strlen(defpath) + 1;
    exists = 0;
  }
  else exists = 1;

  /* Allocate space */

  path = malloc(len + 1);

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

  /* Read the variable or copy the data in */

  if (exists) _swix(OS_ReadVarVal,
                    _INR(0,4),

                    sysvar, /* Variable name                      */
                    path,   /* Buffer                             */
                    len,    /* Size of buffer                     */
                    0,      /* Name pointer (0 for 1st call)      */
                    4);     /* Variable type (4 = literal string) */
  else
  {
    strcpy(path, task_dir);
    strcat(path, defpath);
  }

  /* 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 variable. No need for a default case */
  /* as the first switch statement must have caught that one. */

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

  /* Open the file */

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

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

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

/*************************************************/
/* 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)
{
  /* Set the default window size and position */

  choices.width     = atoi(lookup_choice("DefWidth:1024",0,0));
  choices.height    = atoi(lookup_choice("DefHeight:1280",0,0));
  choices.overridex = atoi(lookup_choice("OverrideX:0",0,0));
  choices.overridey = atoi(lookup_choice("OverrideY:0",0,0));

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

  #ifndef SINGLE_USER

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

  #endif

  /* Set the various default colours */

  choices.col_back = (unsigned int) strtoul(lookup_choice("BackColour:0xdddddd00",0,0),NULL,16);
  choices.col_text = (unsigned int) strtoul(lookup_choice("TextColour:0x00000000",0,0),NULL,16);
  choices.col_link = (unsigned int) strtoul(lookup_choice("LinkColour:0xff000000",0,0),NULL,16);
  choices.col_used = (unsigned int) strtoul(lookup_choice("UsedColour:0xbb008800",0,0),NULL,16);
  choices.col_foll = (unsigned int) strtoul(lookup_choice("FollColour:0x0000ff00",0,0),NULL,16);
  choices.col_sele = (unsigned int) strtoul(lookup_choice("SeleColour:0x00bb0000",0,0),NULL,16);

  /* Default fonts */

  choices.fontsize = atoi(lookup_choice("FontSize:12",0,0));
  if (choices.fontsize < 6)  choices.fontsize = 6;
  if (choices.fontsize > 24) choices.fontsize = 24;
  choices.fontsize *= 16;

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

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

  fixed.poll_delay          = atoi(lookup_control("AnimSpeed:4",       0,0));
  fixed.minimum_convergence = atoi(lookup_control("MinConvergence:480",0,0));
  fixed.show_dstat_for      = atoi(lookup_control("ShowDStatFor:300",  0,0));
  fixed.quantise            = atoi(lookup_control("Quantise:5",        0,0));
  fixed.dontgrey            = atoi(lookup_control("DontGrey:0",        0,0));

  /* Pointer active point offsets */

  {
    int offset;

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

  fixed.backoffat   = atoi(lookup_control("BackOffAt:128",0,0));

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

  if (!strcmp(lookup_control("SwapBars:no"     ,0,0),"yes")) fixed.swapbars     = 1;
  if (!strcmp(lookup_control("DBoxAnims:no"    ,0,0),"yes")) fixed.dboxanims    = 1;

  if (!strcmp(lookup_control("AppendStatus:no" ,0,0),"yes")) fixed.appendstatus = 1;
  if (!strcmp(lookup_control("UseBrackets:yes" ,0,0),"yes")) fixed.usebrackets  = 1;
  if (!strcmp(lookup_control("AppendURLs:no"   ,0,0),"yes")) fixed.appendurls   = 1;

  if (!strcmp(lookup_control("KeepCaret:no"    ,0,0),"yes")) fixed.keepcaret    = 1;
  if (!strcmp(lookup_control("ClaimHelp:no"    ,0,0),"yes")) fixed.claimhelp    = 1;
  if (!strcmp(lookup_control("StopWebServ:no"  ,0,0),"yes")) fixed.stopwebserv  = 1;
  if (!strcmp(lookup_control("BackWindow:no"   ,0,0),"yes")) fixed.backwindow   = 1;

  if (!strcmp(lookup_control("IgnoreAdjust:no" ,0,0),"yes")) fixed.ignoreadjust = 1;
  if (!strcmp(lookup_control("LockToLine:no"   ,0,0),"yes")) fixed.locktoline   = 1;

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

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

  /* Maximum global history size in K, maximum */
  /* view history size in entires, and maximum */
  /* number of images to simultaneously fetch. */

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

  /* History list choices */

  choices.maxghistory = atoi(lookup_choice("GHistSize:8" ,0,0));
  choices.maxvhistory = atoi(lookup_choice("VhistSize:50",0,0));

  if (choices.maxghistory < 1) choices.maxghistory = 1;
  if (choices.maxvhistory < 4) choices.maxvhistory = 4;

  if      (!strcmp(lookup_choice("SaveHistory:once",0,0),"always")) choices.save_history = 2;
  else if (!strcmp(lookup_choice("SaveHistory:once",0,0),"once"))   choices.save_history = 1;

  /* Hotlist options */

  if      (!strcmp(lookup_choice("SaveHotlist:always",0,0),"always")) choices.save_hotlist = 2;
  else if (!strcmp(lookup_choice("SaveHotlist:always",0,0),"once"))   choices.save_hotlist = 1;

  if (!strcmp(lookup_choice("AddHotlist:top",0,0),"bottom"))         choices.add_hotlist  = 1;
  if (!strcmp(lookup_choice("HotlistType:descriptions",0,0),"urls")) choices.hotlist_show = 1;

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

  /* Page layout */

  choices.leftmargin   = atoi(lookup_control("LeftMargin:1600"  ,0,0));
  choices.rightmargin  = atoi(lookup_control("RightMargin:6400" ,0,0));
  choices.quotemargin  = atoi(lookup_control("QuoteMargin:19200",0,0));
  choices.leading      = atoi(lookup_control("Leading:4"        ,0,0));
  choices.leftindent   = atoi(lookup_control("LeftIndent:12800" ,0,0));
  choices.minfrmheight = atoi(lookup_control("MinFrmHeight:48"  ,0,0));
  choices.minfrmwidth  = atoi(lookup_control("MinFrmWidth:48"   ,0,0));

  /* Reformatter options */

  if (!strcmp(lookup_control("RefoWait:no",0,0),"yes")) choices.refowait = 1;
  if (!strcmp(lookup_control("RefoHang:no",0,0),"yes")) choices.refohang = 1;

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

  /* Miscellaneous options */

  if (!strcmp(lookup_control("BrickWall:no"    ,0,0),"yes")) choices.brickwall     = 1;
  if (!strcmp(lookup_control("ClearFirst:yes"  ,0,0),"yes")) choices.clearfirst    = 1;
  if (!strcmp(lookup_control("KeepHighlight:no",0,0),"yes")) choices.keephighlight = 1;

  if (!strcmp(lookup_choice("DelayImage:yes"   ,0,0),"yes")) choices.delayimages   = 1;
  if (!strcmp(lookup_choice("DisplayBgs:yes"   ,0,0),"yes")) choices.displaybgs    = 1;
  if (!strcmp(lookup_choice("UnderlineLks:yes" ,0,0),"yes")) choices.underlinelks  = 1;
  if (!strcmp(lookup_choice("OverrideCols:no"  ,0,0),"yes")) choices.overridecols  = 1;
  if (!strcmp(lookup_choice("ShowURLs:no"      ,0,0),"yes")) choices.show_urls     = 1;
  if (!strcmp(lookup_choice("FixedPtr:yes"     ,0,0),"yes")) choices.fixedptr      = 1;
  if (!strcmp(lookup_choice("HighlightLks:no"  ,0,0),"yes")) choices.highlightlks  = 1;
  if (!strcmp(lookup_choice("KeyboardCtl:no"   ,0,0),"yes")) choices.keyboardctl   = 1;
  if (!strcmp(lookup_choice("UseProxy:no"      ,0,0),"yes")) choices.use_proxy     = 1;
  if (!strcmp(lookup_choice("FullScreen:no"    ,0,0),"yes")) choices.full_screen   = 1;

  if      (!strcmp(lookup_choice("HScroll:no" ,0,0),"no"))  choices.h_scroll = 0;
  else if (!strcmp(lookup_choice("HScroll:no" ,0,0),"yes")) choices.h_scroll = 2;
  else                                                      choices.h_scroll = 1; /* Auto */

  if      (!strcmp(lookup_choice("VScroll:yes",0,0),"no"))  choices.v_scroll = 0;
  else if (!strcmp(lookup_choice("VScroll:yes",0,0),"yes")) choices.v_scroll = 2;
  else                                                      choices.v_scroll = 1; /* Auto */

  /* Set toolbar options */

  if (!strcmp(lookup_choice("DefURLbar:yes"   ,0,0),"yes")) choices.url_bar    = 1;
  if (!strcmp(lookup_choice("DefButtonBar:yes",0,0),"yes")) choices.button_bar = 1;
  if (!strcmp(lookup_choice("DefStatusBar:yes",0,0),"yes")) choices.status_bar = 1;

  if (!strcmp(lookup_choice("MoveGadgets"  ,0,0),"0")) choices.move_gadgets = 0;
  if (!strcmp(lookup_choice("MoveGadgets"  ,0,0),"1")) choices.move_gadgets = 1;
  if (!strcmp(lookup_choice("MoveGadgets:2",0,0),"2")) choices.move_gadgets = 2;

  if      (!strcmp(lookup_control("AnimDrift:no",   0,0),"yes"))    choices.anim_drift   = 1;
  else if (!strcmp(lookup_control("AnimDrift:no",   0,0),"always")) choices.anim_drift   = 2;

  if      (!strcmp(lookup_choice("SolidResize:yes",0,0),"yes"))    choices.solid_resize = 1;
  else if (!strcmp(lookup_choice("SolidResize:yes",0,0),"always")) choices.solid_resize = 2;


  #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.col_back,
                (void *) choices.col_text,
                (void *) choices.col_link,
                (void *) choices.col_used);

      Printf("\nSystem font: %d\n"
               "Delay images: %d\n"
               "Display backgrounds: %d\n"
               "Fixed pointer: %d\n"
               "Underline links: %d\n"
               "Override 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.systemfont,
                choices.delayimages,
                choices.displaybgs,
                choices.fixedptr,
                choices.underlinelks,
                choices.overridecols,
                choices.url_bar,
                choices.button_bar,
                choices.status_bar,
                choices.move_gadgets,
                choices.use_proxy);
    }
  #endif

  /* Wake up the font library */

  fm_init(choices.systemfont, choices.fontsize);
  show_error(fm_define_default_typefaces());
  fm_claim_basic_typefaces(choices.fontsize);

  /* 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 (fixed.claimhelp)
  {
    /* 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.keyboardctl) 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)
  {
    _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 ? 0 : fixed.poll_delay),
                             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) 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) history_save(lookup_choice("HistoryPath:Browse:User.History",0,0));

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

  exit(EXIT_SUCCESS);
}