/* FetchMail v1.41

   Name   : FetchMail
   Version: v1.41b
   Date   : 27th February 1999 / 27th July 2007 for public source release

   Purpose: Fetches your email.

   Creator: Richard Murray.
            Shell created by BudgieSoft SeaShell v1.00 (24 Mar 1997)


   NOTE   : SPLIT INTO TWO PROGRAMS - !RunImage (FetchMail) AND PREPROCESS
   	    AS OF VERSION 1.30.

*/




/* Pragmas */
#pragma warn_implicit_fn_decls
#pragma check_memory_accesses
#pragma optimise_crossjump
#pragma optimise_multiple_loads
#pragma check_stack
#pragma check_printf_formats
#pragma optimise_cse




/* Included files */
/* DeskLib */
#include "DeskLib:WimpSWIs.h" /* Low-level WIMP commands        */
#include "DeskLib:Window.h"   /* Window handling automation     */
#include "DeskLib:Core.h"     /* useful core functions          */
#include "DeskLib:Dialog2.h"  /* windows on menus made easy     */
#include "DeskLib:DragASpr.h" /* Drag sprite handler            */
#include "DeskLib:Error.h"    /* Error handler                  */
#include "DeskLib:Event.h"    /* Event despatcher               */
#include "DeskLib:EventMsg.h" /* Wimp Message event dispatcher  */
#include "DeskLib:File.h"     /* File handling routines         */
#include "DeskLib:Handler.h"  /* Default/example event handlers */
#include "DeskLib:Hourglass.h"/* Hourglass function             */
#include "DeskLib:Icon.h"     /* Icon handling automation       */
#include "DeskLib:Kbd.h"      /* Keyboard (for checking ALT)    */
#include "DeskLib:KernelSWIs.h" /* Kernel SWI definitions       */
#include "DeskLib:Menu.h"     /* Menu create & show support     */
#include "DeskLib:Resource.h" /* Handles finding resource files */
#include "DeskLib:Screen.h"   /* Getting screen size info etc   */
#include "DeskLib:SWI.h"      /* SWI definitions     	  	*/
#include "DeskLib:Time.h"     /* For Time_Monotonic()           */
#include "DeskLib:Template.h" /* Template loading and caching   */
#include "DeskLib:WimpSWIs.h" /* Wimp SWI definitions  	  	*/
/* ANSI C */
#include <ctype.h>            /* Character types                */
#include <stdio.h>            /* Standard input/output etc      */
#include <stdlib.h>           /* Standard additional library    */
#include <signal.h>	      /* For trapping problems		*/
#include <string.h>           /* String handling functions      */
#include <time.h>             /* Time functions                 */
#include "setjmp.h"
#include "stdarg.h"

/* Acorn RISC OS */
#include "kernel.h"           /* Access to RiscOS kernel        */




/* Definitions */




/* Variables */
/* DeskLib */
static icon_handle      iconbaricon;             /* Icon bar handle */
static BOOL finished    = FALSE;                 /* Done yet? */
static dialog2_block    *info;                   /* Info window, as a dialogue box */
static menu_ptr         menu = NULL;	         /* Icon bar menu */
static menu_ptr         usermenu = NULL;	 /* User list menu */
static window_handle    fetchwhich_window;       /* Main (fetch which?) window */
static window_handle    wrongpass_window;        /* Wrong password window */
static window_handle    settings_window;         /* Settings window */
static window_handle    saveas_window;           /* SaveAs window */
/* Other */
static char             pversion[] = "1.41";     /* Version number */
static char		pdate[] = "27th February 1999";/* Date: use original date */
static _kernel_swi_regs r;                       /* SWI registers */
struct userdat                                   /* User data structure definition */
       {
          char username[33];                     /* Username, used for output filename */
          char realname[33];                     /* Realname, used in selection */
          char password[33];                     /* Password, used to ensure correct user */
          char userfile[12];                     /* Userfile, load from correct mailbox */
       };
static struct userdat   *userdata = NULL;        /* User data structure block (blank) */
static int    		which_user = 0;          /* Current user */
static int		which_std_mbx = FALSE;   /* Save standard mailbox flag */
static int		which_sav_mbx = FALSE;   /* Save saved mailbox flag */
static int		which_log_mbx = FALSE;   /* Save log mailbox flag */
static int		which_strip_bin = FALSE; /* Strip binaries flag (linked to sig) */
static int		which_strip_hdr = FALSE; /* Strip headers flag */
static int		which_strip_sig = FALSE; /* Strip signatures flag */
static char		which_savepath[256];     /* Save path */
static int		maximum_users = 0;       /* Maximum number of users */
static char		junk_s[2048];            /* Temp string */
static int		junk_v = 0;              /* Temp variable */
static int 		c_defaultuser = 0;	 /* Default user to load */
static int		c_stdmbox = TRUE;	 /* Default mailbox */
static int		c_logmbox = FALSE;	 /* Default mailbox */
static int		c_svdmbox = FALSE;	 /* Default mailbox */
static int		c_binstrip = FALSE;	 /* Default strip */
static int		c_hdrstrip = FALSE;	 /* Default strip */
static int		c_sigstrip = FALSE;	 /* Default strip */
static int		c_shorthdr = FALSE;	 /* Short headers if header stripped? */
static int		c_p_noreject = FALSE;	 /* Don't allow rejection */
static int		c_p_ignoreua = FALSE;	 /* Ignore UserActions */
static int		c_p_preserve = FALSE;	 /* Preserve deleted messages */
static int		c_p_norecog = FALSE;	 /* Don't show recognised */
static int		c_p_autppre = FALSE;	 /* Automatic preprocessor */
static char 		*crash_msg = "???";      /* Crash message */
static jmp_buf 		posible_travectoria;	 /* Hardwired jump return point */
static int		devmachine = 0;		 /* Is this the development machine? */
static int		uripresent = FALSE;	 /* Is URI handler present? */
static char		url1[]="http://www.heyrick.co.uk/software/oldsource/";
static char 		url2[]="file:/<FetchMail$Dir>/onlinehlp/index.html";
static file_handle	in;                     /* Input file handle */
static file_handle	out;			 /* Output file handle */
static int              dragexport = FALSE;	 /* Dragged to our icon? */

/* Liberal use of junk_s and junk_v is permissable, but such use must NEVER be expected
   to remain constant EVEN between blocks of code. If other (even extraneous) functions
   are called then you should use a local temporary variable. */




/* Function prototypes */
static void initialise_wimp(void);
static void releasememory(void);
static BOOL info_show(event_pollblock *, void *);
static void info_handler(dialog2_block *);
static BOOL iconbar_handler(event_pollblock *, void *);
static BOOL null_handler(event_pollblock *, void *);
static BOOL menu_choice(event_pollblock *, void *);
static BOOL fetchwhich_clickhandler(event_pollblock *, void *);
static BOOL settings_clickhandler(event_pollblock *, void *);
static BOOL wrongpass_clickhandler(event_pollblock *, void *);
static BOOL saveas_clickhandler(event_pollblock *, void *);
static BOOL fetchwhich_keyhandler(event_pollblock *, void *);
static void fetchwhich_draw(void);
static void settings_draw(void);
static void begin_drag(char *, int, int, window_handle, icon_handle, char *, sprite_area);
static BOOL finish_drag(event_pollblock *, void *);
static BOOL save_message(event_pollblock *, void *);
static void read_string(file_handle, char *);
static int  read_word(file_handle);
static void loadusers(void);
static void select_user(int);
static void save_file(void);
static void do_fetchmail(char *, char *, int);
static int  read_coded_line(file_handle, char *);
static int  read_line(char *, file_handle);
static void fetch_username(int, char *);
static void extract_filename(char *, char *);
static void save_settings(void);
static void load_settings(void);
static int  is_binary_start(char *);
static int  is_binary_end(char *);
static void lowercase(char []);
static void bbc_colour(int);
static void bbc_tab(int, int);
static void signl_generic(int);
static void signl_oserr(int);
static void panic(int);
static void coredump(int);
static void trace(char *, ...);
static void build_user_menu(void);
static void export_file(void);
static BOOL dataloader(event_pollblock *, void *);




/* Program entry point */
int main(int argc, char *argv[])
{
   /* Set up signal handlers etc */
   signal(SIGABRT, signl_generic);
   signal(SIGFPE,  signl_generic);
   signal(SIGILL,  signl_generic);
   signal(SIGSEGV, signl_generic);
   signal(SIGTERM, signl_generic);
   signal(SIGSTAK, signl_generic);
   signal(SIGOSERROR, signl_oserr);
   junk_v = setjmp(posible_travectoria);
   if (junk_v != 0) panic(junk_v);

   /* Check if this is the development machine */
   r.r[0] = 129; /* Check OS version */
   r.r[1] = 0;
   r.r[2] = 255;
   _kernel_swi(SWI_OS_Byte, &r, &r);
   if (r.r[1] >= 0xA3)
   {
      r.r[0] = 2;
      r.r[1] = 0;
      r.r[2] = 0;
      _kernel_swi(SWI_OS_ReadSysInfo, &r, &r);
      if ((r.r[3] == 0x7EB39) && (r.r[4] == 0)) devmachine = 1;
   }

   /* Set up Resources pointer... */
   Resource_Initialise("FetchMail");
   strcpy(resource_pathname, "<FetchMail$Dir>.Resources.");

   /* Command line parameters */
   if (argc > 1)
   {
      if (strcmp(argv[1], "-preprocess") == NULL)
      {
         Error_Report(1, "The preprocessor is now a seperate program. Please amend your Obey files and/or software to account for this.");
         exit(0);
      }
   }

   /* Error_Report(1, "If your legal given name isn't \"Richard Goodwin\" or \"Richard Murray\", please sod off. Thank you."); */

   /* Claim the memory block for forty user definitions */
   userdata = calloc(40, sizeof(struct userdat));
   if (!userdata)
   {
      Error_ReportFatal(1, "unable to claim memory for user data structure");
   }
   atexit(releasememory);

   /* Initialise ourselves as a WIMP task */
   Event_Initialise("FetchMail");
   initialise_wimp();

   /* Load options */
   load_settings();

   /* Load in user data and select default user */
   loadusers();
   build_user_menu();
   select_user(c_defaultuser);

   /* Poll loop. Uses idle. Less load on system. */
   while( !finished )
   {
      Wimp_PollIdle(event_mask, &event_lastevent, (Time_Monotonic() + 5000));
      Event_Process(&event_lastevent);
   }

   /* Introducing Trevor McDonald's "And finally..." tour! */
   Template_ClearAll();
   Event_CloseDown();
   exit(EXIT_SUCCESS);
}



static void initialise_wimp(void)
{
   /* Initialise the libraries and set up associated crap */
   Resource_LoadSprites();
   EventMsg_Initialise();
   Screen_CacheModeInfo();
   Template_Initialise();
   Template_UseSpriteArea(resource_sprites);
   Template_LoadFile("F_template");

   /* Define our menu */
   menu = Menu_New("FetchMail", ">Info,Settings,Quit");
   Event_Claim(event_MENU, event_ANY, event_ANY, menu_choice, NULL);

   /* Load info window. It's a transient dialogue box (that's dialogue with a
      great big British sized "ue" at the end! :-) ) */
   info = Dialog2_CreateDialogBlock("info", -1, -1, info_handler, NULL, NULL);
   Menu_Warn(menu, 0, TRUE, info_show, info);

   /* Load fetchwhich window */
   fetchwhich_window = Window_Create("fetchwhich", 45);
   if (fetchwhich_window == 0)
   {
      Error_ReportFatal(0, "cannot find template definition '%s'", "fetchwhich");
   }
   Event_Claim(event_CLICK, fetchwhich_window, event_ANY, fetchwhich_clickhandler, NULL);
   Event_Claim(event_KEY, fetchwhich_window, event_ANY, fetchwhich_keyhandler, NULL);

   /* Load settings window */
   settings_window = Window_Create("settings", 45);
   if (settings_window == 0)
   {
      Error_ReportFatal(0, "cannot find template definition '%s'", "settings");
   }
   Event_Claim(event_CLICK, settings_window, event_ANY, settings_clickhandler, NULL);

   /* Load wrongpass window */
   wrongpass_window = Window_Create("wrongpass", 45);
   if (wrongpass_window == 0)
   {
      Error_ReportFatal(0, "cannot find template definition '%s'", "wrongpass");
   }
   Event_Claim(event_CLICK, wrongpass_window, event_ANY, wrongpass_clickhandler, NULL);

   /* Load saveas window */
   saveas_window = Window_Create("saveas", 45);
   if (saveas_window == 0)
   {
      Error_ReportFatal(0, "cannot find template definition '%s'", "saveas");
   }
   Event_Claim(event_CLICK, saveas_window, event_ANY, saveas_clickhandler, NULL);

   /* Set up the default handlers to save me working it out the long way... :-) */
   Event_Claim(event_CLOSE, event_ANY, event_ANY, Handler_CloseWindow, NULL);
   Event_Claim(event_REDRAW, event_ANY, event_ANY, Handler_NullRedraw, NULL);
   Event_Claim(event_OPEN, event_ANY, event_ANY, Handler_OpenWindow, NULL);
   Event_Claim(event_USERDRAG, event_ANY, event_ANY, finish_drag, NULL);

   iconbaricon = Icon_BarIconUser("!fetchmail", iconbar_RIGHT, (unsigned int *)resource_sprites);
   Event_Claim(event_CLICK, -2, iconbaricon, iconbar_handler, NULL);

   /* Other useful stuff we might /just/ be interested in knowing about */
   EventMsg_Claim(message_MODECHANGE, event_ANY, Handler_ModeChange, NULL);
   EventMsg_Claim(message_DATASAVE, event_ANY, save_message, NULL);
   EventMsg_Claim(message_DATASAVEACK, event_ANY, save_message, NULL);
   EventMsg_Claim(message_RAMFETCH, event_ANY, save_message, NULL);
   EventMsg_Claim(message_DATALOAD, event_ANY, dataloader, NULL);

   return;
}



static void releasememory(void)
{
   /* Baaaaaaaa! */
   if (userdata)
   {
      free(userdata);
      userdata = NULL;
   }

   return;
}



static BOOL info_show(event_pollblock *event, void *reference)
{
   /* This routine wants to be a function when it grows up */
   Dialog2_OpenDialogMenuLeaf(event, (dialog2_block *) reference);

   return TRUE;
}



static void info_handler(dialog2_block *dialog2)
{
   /* Patch the correct version into the info window */

   Icon_printf(dialog2->window, 4, "%s %s", pversion, pdate);

   return;
}



static BOOL iconbar_handler(event_pollblock *event, void *reference)
{
   /* If you click SELECT, open the fetchwhich window. MENU opens the menu (like duh!)
      and ADJUST opens the configuration window.
   */

   reference = reference;
   Window_Hide(saveas_window);

   if (event->data.mouse.button.data.select)
   {

      fetchwhich_draw();
      Window_Show(fetchwhich_window, open_CENTERED);
   }

   if (event->data.mouse.button.data.menu)
   {
      Menu_Show(menu, event->data.mouse.pos.x,-1);
   }

   if (event->data.mouse.button.data.adjust)
   {
      settings_draw();
      Window_Show(settings_window, open_CENTERED);
   }

   return TRUE;
}



static BOOL null_handler(event_pollblock *event, void *reference)
{
   /* Does nothing, allows 'null poll' routines. */

   reference = reference;
   event = event;

   return TRUE;
}



static BOOL menu_choice(event_pollblock *event, void *reference)
{
   /* Deals with menu selections. This one won't wet itself if the menu definition
      is 'broken'. Then again, perhaps it should -- teach the user to hack only when
      they know what they are doing!
   */

   mouse_block ptr;

   if (menu_currentopen == menu)
   {
      switch(event->data.selection[0])
      {
         case 0 : Dialog2_OpenDialogMenuLeaf(event, (dialog2_block *)reference);
                  break;
         case 1 : settings_draw();
                  Window_Show(settings_window, open_CENTERED);
                  break;
         case 2 : finished = TRUE;
                  break;
         default: break;
      }
   }

   if (menu_currentopen == usermenu)
   {
      c_defaultuser = event->data.selection[0];
      Icon_SetText(settings_window, 3, userdata[c_defaultuser].realname);
   }

   Wimp_GetPointerInfo(&ptr);  /* If ADJUST was used to select, then keep the menu open. */
   if (ptr.button.data.adjust) Menu_ShowLast();

   return TRUE;
}



static BOOL fetchwhich_clickhandler(event_pollblock *event, void *reference)
{
   int icon = -1;

   reference = reference;

   icon = event->data.mouse.icon;

   Window_Hide(saveas_window);

   if ((event->data.mouse.button.data.select) || (event->data.mouse.button.data.adjust))
   {
      icon = event->data.mouse.icon;

      switch(icon)
      {
         case  4 : which_user++;
               	   if (which_user > maximum_users) which_user = 0;
               	   select_user(which_user);
               	   break;
         case  5 : which_user--;
               	   if (which_user < 0) which_user = maximum_users;
               	   select_user(which_user);
               	   break;
         case  6 : which_std_mbx = which_std_mbx ^ TRUE;
               	   Icon_SetSelect(fetchwhich_window, 6, which_std_mbx);
               	   break;
         case  7 : which_sav_mbx = which_sav_mbx ^ TRUE;
               	   Icon_SetSelect(fetchwhich_window, 7, which_sav_mbx);
               	   break;
         case  8 : which_log_mbx = which_log_mbx ^ TRUE;
               	   Icon_SetSelect(fetchwhich_window, 8, which_log_mbx);
               	   break;
         case  9 : which_strip_bin = which_strip_bin ^ TRUE;
               	   Icon_SetSelect(fetchwhich_window, 9, which_strip_bin);
               	   break;
         case 10 : which_strip_hdr = which_strip_hdr ^ TRUE;
               	   Icon_SetSelect(fetchwhich_window, 10, which_strip_hdr);
               	   break;
         case 11 : which_strip_sig = which_strip_sig ^ TRUE;
               	   Icon_SetSelect(fetchwhich_window, 11, which_strip_sig);
               	   break;
         case 14 : /* if (event->data.mouse.button.value == button_DRAGSELECT) */
              	   {
              	      begin_drag(Icon_GetTextPtr(fetchwhich_window, 13), 0x1000, 1024, fetchwhich_window, 14, "directory", 0); /* Don't uncomment the DRAGSELECT line above! */
              	   }
              	   break;
         case 15 : save_file();
              	   break;
         case 16 : Window_Hide(fetchwhich_window);
              	   break;
         case 19 : Error_Report(1, "Sorry, this facility is not yet available.");
                   /* verify_flags(); */
              	   break;
         default : break;
      }
   }

   return TRUE;
}



static BOOL settings_clickhandler(event_pollblock *event, void *reference)
{
   /* Handles mouse clicks on the 'settings' window */

   int icon = -1;
   int alt = FALSE;

   reference = reference;
   icon = event->data.mouse.icon;

   if (Kbd_KeyDown(inkey_ALT)) alt = TRUE;

   if ((event->data.mouse.button.data.select) || (event->data.mouse.button.data.adjust))
   {
      icon = event->data.mouse.icon;

      switch(icon)
      {
         case  4 : Menu_Show(usermenu, event->data.mouse.pos.x, event->data.mouse.pos.y);
                   break;

         case  6 : c_stdmbox = c_stdmbox ^ TRUE;
                   Icon_SetSelect(settings_window, 6, c_stdmbox);
                   break;

         case  7 : c_svdmbox = c_svdmbox ^ TRUE;
                   Icon_SetSelect(settings_window, 7, c_svdmbox);
                   break;

         case  8 : c_logmbox = c_logmbox ^ TRUE;
                   Icon_SetSelect(settings_window, 8, c_logmbox);
                   break;

         case 10 : c_binstrip = c_binstrip ^ TRUE;
                   Icon_SetSelect(settings_window, 10, c_binstrip);
                   break;

         case 11 : c_hdrstrip = c_hdrstrip ^ TRUE;
                   Icon_SetSelect(settings_window, 11, c_hdrstrip);
                   break;

         case 12 : c_sigstrip = c_sigstrip ^ TRUE;
                   Icon_SetSelect(settings_window, 12, c_sigstrip);
                   break;

         case 17 : c_shorthdr = c_shorthdr ^ TRUE;
                   Icon_SetSelect(settings_window, 17, c_shorthdr);
                   break;

         case 15 : c_p_noreject = c_p_noreject ^ TRUE;
                   Icon_SetSelect(settings_window, 15, c_p_noreject);
                   break;

         case 16 : c_p_ignoreua = c_p_ignoreua ^ TRUE;
                   Icon_SetSelect(settings_window, 16, c_p_ignoreua);
                   break;

         case 18 : c_p_preserve = c_p_preserve ^ TRUE;
                   Icon_SetSelect(settings_window, 18, c_p_preserve);
                   break;

         case 19 : c_p_norecog = c_p_norecog ^ TRUE;
                   Icon_SetSelect(settings_window, 19, c_p_norecog);
                   break;

         case 20 : c_p_autppre = c_p_autppre ^ TRUE;
                   Icon_SetSelect(settings_window, 20, c_p_autppre);
                   break;

         case 21 : save_settings();
              	   Window_Hide(settings_window);
              	   break;

         case 22 : load_settings();
              	   Window_Hide(settings_window);
              	   break;

         case 32 : r.r[1] = (int)url1; /* 1 - FetchMail WWW area */
              	   _kernel_swi(0x4E381, &r, &r); /* URI_Dispatch */
              	   break;
         case 33 : r.r[1] = (int)url2; /* 2 - On-line documentation */
              	   _kernel_swi(0x4E381, &r, &r); /* URI_Dispatch */
              	   break;

         default : break;
      }
   }

   return TRUE;
}



static BOOL wrongpass_clickhandler(event_pollblock *event, void *reference)
{
   /* Any click on this window will hide it... */
   reference = reference;

   if ((event->data.mouse.button.data.select) || (event->data.mouse.button.data.adjust))
   {
      Window_Hide(wrongpass_window);
   }

   /* A little something buried in the code */
   if (junk_v == 318504)
   {
      printf("\n\nSometimes you're better off dead.\nThere's a gun in your hand and it's pointing at your head.\nThink you're mad, too unstable,\nkicking in chairs and knocking down tables;\nin a restaurant, in a west end town.\nCall the police, there's a madman around. \nRun him down, underground, to a dive bar in a west end town...\n\n");
      /* No brownie points for knowing the song - like WHERE were you in the eighties? */
   }

   return TRUE;
}



static BOOL saveas_clickhandler(event_pollblock *event, void *reference)
{
   /* Handles mouse clicks on the 'fetchwhich' window */

   int icon = -1;

   reference = reference;
   icon = event->data.mouse.icon;

   if ((event->data.mouse.button.data.select) || (event->data.mouse.button.data.adjust))
   {
      icon = event->data.mouse.icon;

      switch(icon)
      {
         case  0 : dragexport = TRUE;
                   begin_drag(Icon_GetTextPtr(saveas_window, 1), 0xFFF, 1024, saveas_window, 0, "file_fff", 0);
              	   break;
         case  2 : export_file();
              	   break;
         default : break;
      }
   }

   return TRUE;
}



static BOOL fetchwhich_keyhandler(event_pollblock *event, void *reference)
{
   /* Handles key presses on the 'fetchwhich' window */

   int key  = -1;

   reference = reference;

   key = event->data.key.code;

   if (key > 256) Wimp_ProcessKey(key);

   return TRUE;
}



static void fetchwhich_draw(void)
{
   /* Updates the contents of 'fetchwhich' just before it is opened. */

   which_user = c_defaultuser;
   select_user(which_user);
   which_std_mbx = c_stdmbox;
   Icon_SetSelect(fetchwhich_window, 6, which_std_mbx);
   which_sav_mbx = c_svdmbox;
   Icon_SetSelect(fetchwhich_window, 7, which_sav_mbx);
   which_log_mbx = c_logmbox;
   Icon_SetSelect(fetchwhich_window, 8, which_log_mbx);
   which_strip_bin = c_binstrip;
   Icon_SetSelect(fetchwhich_window, 9, which_strip_bin);
   which_strip_hdr = c_hdrstrip;
   Icon_SetSelect(fetchwhich_window, 10, which_strip_hdr);
   which_strip_sig = c_sigstrip;
   Icon_SetSelect(fetchwhich_window, 11, which_strip_sig);

   return;
}



static void settings_draw(void)
{
   /* Updates the contents of 'settings' just before it is opened. */

   Icon_SetText(settings_window, 3, userdata[c_defaultuser].realname);
   Icon_SetSelect(settings_window, 6, c_stdmbox);
   Icon_SetSelect(settings_window, 7, c_svdmbox);
   Icon_SetSelect(settings_window, 8, c_logmbox);
   Icon_SetSelect(settings_window, 10, c_binstrip);
   Icon_SetSelect(settings_window, 11, c_hdrstrip);
   Icon_SetSelect(settings_window, 12, c_sigstrip);
   Icon_SetSelect(settings_window, 17, c_shorthdr);
   Icon_SetSelect(settings_window, 15, c_p_noreject);
   Icon_SetSelect(settings_window, 16, c_p_ignoreua);
   Icon_SetSelect(settings_window, 18, c_p_preserve);
   Icon_SetSelect(settings_window, 19, c_p_norecog);
   Icon_SetSelect(settings_window, 20, c_p_autppre);

   return;
}



static void begin_drag(char *filename, int filetype, int filesize, window_handle window, icon_handle icon, char *iconname, sprite_area area)
{
   /* Messy? Sure as hell is. Couldn't figure out the drag library stuff though. Kept on
      moaning about pointer to non-zero pointer. WTF? Ah well, at least this way you can
      see what is going on. */

   int solid;
   wimp_rect bound;
   int block[20];
   int winminx, winminy, winmaxx, winmaxy;

   /* Remove compiler warnings. I'm sure I was going to do something useful
      with these variables... Can't remember. Ho hum! */
   area = area;
   filesize = filesize;
   filetype = filetype;
   filename = filename;

   /* Solid drag? */
   OS_Byte(osbyte_READCMOSRAM, 28, 0, NULL, &solid);

   block[0] = window;
   r.r[1] = (int) block;
   _kernel_swi( SWI_XOS_Bit | 0x0400CB, &r, &r ); /* Wimp_GetWindowState */
   winminx = block[1];
   winminy = block[2];
   winmaxx = block[3];
   winmaxy = block[4];

   block[0] = window;
   block[1] = icon;
   r.r[1] = (int) block;
   _kernel_swi( SWI_XOS_Bit | 0x0400CE, &r, &r ); /* Wimp_GetIconState */

   bound.min.x = winminx + block[2];
   bound.max.x = winminx + block[4];
   bound.min.y = winminy + block[3] + (winmaxy - winminy);
   bound.max.y = winminy + block[5] + (winmaxy - winminy);

   if ((solid & 2) == 2)
   {
      DragASprite_Start(0xC5, (void *) 1, iconname, &bound, (wimp_rect *) 0);
   }

   return;
}



static BOOL finish_drag(event_pollblock *event, void *reference)
{
   /* Tidies up the drag action. */
   int  solid;
   mouse_block    ptr;
   char filenm[12];

   reference = reference;

   OS_Byte(osbyte_READCMOSRAM, 28, 0, NULL, &solid);

   if ((solid & 2) == 2)
      DragASprite_Stop();
   else
      Wimp_DragBox((drag_block *) -1);

   Wimp_GetPointerInfo( &ptr);

   event->data.message.header.size            = 80;
   event->data.message.header.yourref         = 0;
   event->data.message.header.action          = message_DATASAVE;
   event->data.message.data.datasave.window   = ptr.window;
   event->data.message.data.datasave.icon     = ptr.icon;
   event->data.message.data.datasave.pos      = ptr.pos;
   event->data.message.data.datasave.estsize  = 1024;
   event->data.message.data.datasave.filetype = 0xfff;


   /*strcpy(event->data.message.data.datasave.leafname, userdata[which_user].username); */
   if (dragexport == FALSE)
   {
      Icon_GetText(fetchwhich_window, 13, junk_s);
      extract_filename(junk_s, filenm);
      strcpy(event->data.message.data.datasave.leafname, filenm);
   }
   else
   {
      Icon_GetText(saveas_window, 1, junk_s);
      extract_filename(junk_s, filenm);
      strcpy(event->data.message.data.datasave.leafname, filenm);
   }

   Wimp_SendMessage(event_SENDWANTACK, &event->data.message, (message_destinee) ptr.window, ptr.icon);

  return(TRUE);
}



static BOOL save_message(event_pollblock *event, void *reference)
{
   /* Deals with save related messages...

      NOTE: As the file is built up line by line and not held in memory, a RAMtransfer
            is NOT supported.
   */

   reference = reference;

   switch(event->data.message.header.action)
   {
      case message_DATASAVE :
         if (event->data.message.header.sender != event_taskhandle)
         {
            strcpy(event->data.message.data.datasave.leafname, "<Wimp$Scrap>");
            event->data.message.data.datasave.estsize = -1;
            event->data.message.header.yourref = event->data.message.header.myref;
            event->data.message.header.action = message_DATASAVEACK;
            event->data.message.header.size = 60;
            Wimp_SendMessage(event_USERMESSAGE, &event->data.message, event->data.message.header.sender, 0);
         }
         return(TRUE);
         break;

      case message_DATASAVEACK :
         event->data.message.data.datasaveack.estsize = 1024;
         event->data.message.header.yourref = event->data.message.header.myref;
         event->data.message.header.action = message_DATALOAD;
         Wimp_SendMessage(event_USERMESSAGERECORDED, &event->data.message, event->data.message.data.datasaveack.window, event->data.message.data.datasaveack.icon);
         if (dragexport == FALSE)
         {
            Icon_SetText(fetchwhich_window, 13, event->data.message.data.datasaveack.filename);
            Wimp_CreateMenu((menu_ptr) -1, 0, 0); /* remove any menu from screen */
            save_file();
            return(TRUE);
         }
         else
         {
            Icon_SetText(saveas_window, 1, event->data.message.data.datasaveack.filename);
            Wimp_CreateMenu((menu_ptr) -1, 0, 0); /* remove any menu from screen */
            export_file();
            dragexport = FALSE;
            return(TRUE);
         }
         break;
   }

   return(FALSE);
}



static void read_string(file_handle file, char *string)
{
   /* Reads a string in PRINT#/INPUT# format */

   int len=0;
   int i=0;

   i = File_ReadByte(file);
   if (i != 0x00)
   {
      File_Close(file);
      Error_ReportFatal(1, "error trying to read string, couldn't find type byte (&00)");
   }

   len = File_ReadByte(file);
   if (len>0)
   {
      for (i=len-1;i>=0;i--)
      {
         string[i] = File_ReadByte(file);
      }
      string[len] = '\0';
   }
}



static int read_word(file_handle file)
{
   /* Reads a word in PRINT#/INPUT# format */

   int num=0;

   num = File_ReadByte(file);
   if (num != 0x40)
   {
      File_Close(file);
      Error_ReportFatal(1, "error trying to read word, couldn't find type byte (&40)");
   }

   num = File_ReadByte(file);
   num = (num << 8) | File_ReadByte(file);
   num = (num << 8) | File_ReadByte(file);
   num = (num << 8) | File_ReadByte(file);
   return num;
}



static void loadusers(void)
{
   /* Loads in the user data. Type 2 and type 3 files supported. DTPosty had a fallback
      to a different format. But, what happens upon type 4? :-)

      File is:
         <STRING>  username
         <STRING>  real name
         <STRING>  userfile
         <WORD>    user has new mail?
         <WORD>    size of mailbox?
         <WORD>    user inactive?

         and in type 3 only:
         <STRING>  password

      Not that my opinion counts for anything, but why are Argo making such a fuss
      over the protection scheme? It's a piece of p.....you get the idea.

      Here's my suggestion:
        Store in the file the numerical value of the password through a one-way
        process, be it OS_CRC or a custom 'hash table'.
        The mail file is not encoded with constant EOR &21, but is EORd with the
        byte proceeding. The initial value is ((pass% >> 8) EOR (pass% AND &FF)).
        A tad slower, not too much more complex, but harder for a casual hack as
        the pattern is MUCH harder to follow.
   */

   file_handle fp;
   int  type = 0;

   /* Looks for "<PostyUser$Dir>". Requires Posty having been run so variable is set up.
      Could use access relative to "<VTiInternet$Dir>". Have to contact Argo to ensure this
      won't fall over... */

   fp = File_Open("<PostyUser$Dir>.Users", file_READ);
   if (!fp) Error_ReportFatal(1, "cannot load Posty's Users file, does it exist?");

   read_string(fp, junk_s);
   if (strcmp(junk_s, " #2") == NULL) type = 2;
   if (strcmp(junk_s, " #3") == NULL) type = 3;
   if (type == 0) Error_ReportFatal(1, "unrecognised Users file format, which version of Posty are you using?");

   maximum_users = -1;
   do
   {
      maximum_users++;

      read_string(fp, userdata[maximum_users].username);
      read_string(fp, userdata[maximum_users].realname);
      read_string(fp, userdata[maximum_users].userfile);
      junk_v = read_word(fp);
      junk_v = read_word(fp);
      junk_v = read_word(fp);
      if (type == 2)
         strcpy(userdata[maximum_users].password, "");
      else
         read_string(fp, userdata[maximum_users].password);

   } while ( !File_EOF(fp) && (maximum_users < 40));


   File_Close(fp);

   return;
}



static void select_user(int which)
{
   /* Sets up user selection. No big deal. */
   char userid[20];

   fetch_username(which, userid);
   Icon_SetText(fetchwhich_window, 3, userdata[which].realname);
   Icon_SetText(fetchwhich_window, 18, "");
   Icon_SetText(fetchwhich_window, 13, userid);

   /* More stuff buried in the code */
   if (junk_v == 318504)
   {
      printf("\n\nYes! I do know how I survived.\nYes! I do know why I'm alive.\nTo love and be with you, day by day by day by day-ay...\n\n");
      /* oh look, more song lyrics... */
   }

   return;
}



static void save_file(void)
{
   /* Selects what to save, and calls function. */
   char filename[256];

   /* Ascertain filename */
   Icon_GetText(fetchwhich_window, 13, filename);

   /* Verify password */
   Icon_GetText(fetchwhich_window, 18, junk_s);
   if (strcmp(junk_s, userdata[which_user].password) != NULL)
   {
      /* If not correct, open warning window, then outtie. */
      Window_Show(wrongpass_window, open_CENTERED);
      return;
   }

   /* Prompt warning if user hasn't selected WHAT to save. Doesn't outtie because
      in this condition, do_fetchmail() won't be called. Easy! */
   if ( !which_std_mbx && !which_sav_mbx && !which_log_mbx) Error_Report(1, "Please choose which mailbox(es) you require fetching.");

   /* Create base directory. */
   sprintf(junk_s, "%%CDir %s", filename);
   r.r[0] = (int)junk_s;
   _kernel_swi(SWI_OS_CLI, &r, &r);

   /* Work out what to do, and get it done. */
   if (which_std_mbx) do_fetchmail("Standard", filename, 1);
   if (which_sav_mbx) do_fetchmail("Saved", filename, 2);
   if (which_log_mbx) do_fetchmail("Log", filename, 3);

   return;
}



static void do_fetchmail(char *msgtype, char *filename, int ledtype)
{
   /* Parse input line by line dealing with it as follow-through. This routine must NOT
      poll. It'd be nice for animations, but testing is showing the speed is good enough
      that the cute Windowsesque gimmicks are not needed... And this is on an ARM 3! */

   int  currently_sig = FALSE;
   int  currently_bin = FALSE;
   int  currently_hdr = FALSE;
   char line[85];
   int  filelen = 0;
   int  showline = TRUE;

   sprintf(junk_s, "%s.%s", filename, msgtype);

   out = File_Open(junk_s, file_WRITE);
   if (out == NULL)
   {
      Error_Report(1, "Unable to open the output file, does another program have it open?");
      return;
   }

   /* Set up the message path required from system variables... */
   switch (ledtype)
   {
      case 1 : _kernel_getenv("FetchMail$Path$Std", junk_s, 255);
      	       break;
      case 2 : _kernel_getenv("FetchMail$Path$Sav", junk_s, 255);
      	       break;
      case 3 : _kernel_getenv("FetchMail$Path$Log", junk_s, 255);
      	       break;
      default: Error_ReportFatal(1, "error in do_fetchmail() - unknown type");
      	       break;
   }
   strcat(junk_s, userdata[which_user].userfile);

   in = File_Open(junk_s, file_READ);
   if (in == NULL)
   {
      Error_Report(1, "Unable to open the %s mailbox, does it exist?", msgtype);
      File_Close(out);
      /* Remove this file, to be tidy... */
      sprintf(junk_s, "%s.%s", filename, msgtype);
      remove(junk_s);
      return;
   }

   Hourglass_On();
   /* r.r[0] = ledtype;
   _kernel_swi(0x406C5, &r, &r);  Hourglass_LEDs */

   filelen = File_ReadExtent(in);

   if (filelen == 0)
   {
      Error_Report(1, "Your %s mailbox is empty, skipping...", msgtype);
      File_Close(in);
      File_Close(out);
      return;
   }

   do
   {
      /* Current byte by line conversion with no intelligence. Slow? Nope. BASIC is though.
         Just proves interpreted languages can take a flying f...<ahem!> when it comes to
         fast-executing repetitive stuff (like this).
      */

      /* Does a %ge hourglass so the user knows we haven't crashed - this has enough
         everheads as it is... */
      junk_v = ((int)File_ReturnPos(in) * 100) / filelen;
      Hourglass_Percentage(junk_v);

      /* Get the line */
      r.r[0] = 1;
      _kernel_swi(0x406C5, &r, &r); /* Hourglass_LEDs */
      junk_v = read_coded_line(in, line);

      /* Check for new message, flag header unset rest */
      if (strstr(line, "#! rmail") != NULL)
      {
         currently_sig = FALSE;
         currently_bin = FALSE;
         currently_hdr = TRUE;
      }

      /* Check for end of header if in header */
      if (currently_hdr && (line[0] == '\0')) currently_hdr = FALSE;

      /* Check for beginning/end of UUcode */
      if (is_binary_start(line) == TRUE) currently_bin = TRUE;
      if (currently_bin && (is_binary_end(line) == TRUE)) currently_bin = FALSE;

      /* Check for sig. Sig is anything from "-- " to new message. */
      if (strstr(line, "-- ") != NULL) currently_sig = TRUE;

      /* Set showline to TRUE, and undo it if a clause fails. */
      showline = TRUE;
      if (which_strip_sig && currently_sig) showline = FALSE;
      if (which_strip_hdr && currently_hdr) showline = FALSE;
      if (which_strip_bin && currently_bin) showline = FALSE;

      /* If passed, output */
      r.r[0] = 2;
      _kernel_swi(0x406C5, &r, &r); /* Hourglass_LEDs */
      if (showline) File_printf(out, "%s\n", line);

   } while (junk_v >= 0);

   /* A little ID :-) */
   File_printf(out, "File created by FetchMail v%s.\n", pversion);

   File_Close(in);
   File_Close(out);

   /* SetType so the more braindead systems treat it properly... Also helps get !Zap
      open it as text and not a byte dump. :-) */
   sprintf(junk_s, "%%SetType %s.%s Text", filename, msgtype);
   r.r[0] = (int)junk_s;
   _kernel_swi(SWI_OS_CLI, &r, &r);

   Hourglass_Off();

   return;
}



static int  read_coded_line(file_handle hdl, char *line)
{
   /* Read a coded line and return when reach &10 (Newline). */

   /* NOTE : End of file (-1) EORed with &21 is -34 */

   int  byte = 0;
   int  counter = 0;

   line[0] = '\0';
   do
   {
      byte = File_ReadByte(hdl);
      byte = byte ^ 0x21;

      if (byte > 31)
      {
         line[counter] = byte;
         counter++;
         line[counter] = '\0';
      }
   } while (byte != 10 && byte >=0 && (strlen(line) < 80));

   /* Set the end of string null in the loop. It's a little bit slower
      but it allows us to follow through the string length. */

   return byte;
}



static int  read_line(char *line, file_handle hdl)
{
   /* Read a line and return TRUE if EOF/error condition or FALSE if okay. */
   int eofcond = FALSE;
   int byte = 0;
   int offset = 0;

   offset = 0;
   line[offset] = '\0';

   do
   {
      byte = File_ReadByte(hdl);
      if (byte > 31) line[offset] = byte;
      offset++;
   } while (byte > 31);
   line[offset] = '\0';

   if (eofcond == 1) line[0] = '\0';

   /* Weird code huh?
      The newline character is (wisely?) retained, so here we strip it off.
   */

   if (byte == -1) eofcond = TRUE;

   return eofcond;
}



static void fetch_username(int which, char *out)
{
   /* Some users can have weird names in the form "diddly.squat@argonet.co.uk".
      These names will upset ADFS, so we fetch the username and replace the
      period with an underscore. Change also slash, hash, dollar, splat and
      percent for good measure.
   */

   int  loop = 0;
   int  len = 0;

   strcpy(junk_s, userdata[which].username);

   len = strlen(junk_s);
   for (loop = 0; loop <= len; loop++)
   {
      switch(junk_s[loop])
      {
         case '.' : out[loop] = '_';
         	    break;
         case '/' : out[loop] = '_';
         	    break;
         case '#' : out[loop] = '_';
         	    break;
         case '$' : out[loop] = '_';
         	    break;
         case '*' : out[loop] = '_';
         	    break;
         case '%' : out[loop] = '_';
         	    break;
         default  : out[loop] = junk_s[loop];
         	    break;
      }
   }
   out[loop] = '\0';
   out[10] = '\0';    /* Force truncate to 10 chars if over, prolly wilbee */

   return;
}



static void extract_filename(char *in, char *out)
{
   /* Extract the filename from the path */
   int  len = 0;
   int  pos = 0;
   int  count = 0;

   pos = strlen(in);
   len = strlen(in);

   do                        /* Scan from end looking for period */
   {
      pos--;
   } while ((in[pos] != '.') &&  (pos > 0));

   if (pos == 0)             /* No period? Copy input as output and return */
   {
      strcpy(out, in);
      return;
   }

   pos++;                    /* Move up past the period */

   for (;pos <= len; pos++)  /* ...and copy as output */
   {
      out[count] = in[pos];
      count++;
   }
   out[count] = '\0';

   return;
}



static void save_settings(void)
{
   /* Save settings */
   file_handle out;

   out = File_Open("<FetchMail$Dir>.Resources.Options", file_WRITE);
   if (out == NULL)
   {
      Error_Report(1, "Unable to save the Options file, does another program have it open?");
      return;
   }

   File_WriteByte(out, 2);                              /* Byte  : File version        */
   File_WriteByte(out, 10);                             /* Bring it down a line        */
   File_WriteByte(out, c_defaultuser);                  /* Byte  : Default user        */
   File_WriteByte(out, c_stdmbox);                      /* Byte  : Default mailbox     */
   File_WriteByte(out, c_logmbox);                      /* Byte  : Default mailbox     */
   File_WriteByte(out, c_svdmbox);                      /* Byte  : Default mailbox     */
   File_WriteByte(out, c_binstrip);                     /* Byte  : Default strip       */
   File_WriteByte(out, c_hdrstrip);                     /* Byte  : Default strip       */
   File_WriteByte(out, c_sigstrip);                     /* Byte  : Default strip       */
   File_WriteByte(out, c_shorthdr);                     /* Byte  : Short headers?      */
   File_WriteByte(out, c_p_noreject);                   /* Byte  : No rejections       */
   File_WriteByte(out, c_p_ignoreua);                   /* Byte  : Ignore 'UserAction' */
   File_WriteByte(out, c_p_preserve);                   /* Byte  : Preserve deleted    */
   File_WriteByte(out, c_p_norecog);                    /* Byte  : Don't show recog'd  */
   File_WriteByte(out, c_p_autppre); 		        /* Byte  : Automatic preproc.  */
   File_printf(out, "\n\nFetchMail options file, created by version %s.\n\n", pversion);
   File_Close(out);

   return;
}



static void load_settings(void)
{
   /* Load settings.
      MUST be called AFTER wimp init because it uses/alters templates.
   */

   in = File_Open("<FetchMail$Dir>.Resources.Options", file_READ);
   if (in == NULL)
   {
      Error_Report(1, "Unable to load from the Options file, does it exist?");
      return;
   }

   junk_v = File_ReadByte(in);
   if (junk_v < 2)
   {
      Error_Report(1, "You are trying to use the old format Options file (versions prior to 1.30) which is not compatible with this version of FetchMail.");
      File_Close(in);
      settings_draw();
      Window_Show(settings_window, open_CENTERED);
      return;
   }

   if (junk_v > 2)
   {
      Error_Report(1, "You are trying to use an unrecognised options file (format byte is '%d', expecting '2').", junk_v);
      File_Close(in);
      settings_draw();
      Window_Show(settings_window, open_CENTERED);
      return;
   }

   junk_v = File_ReadByte(in); /* Skip newline */

   c_defaultuser = File_ReadByte(in);
   c_stdmbox = File_ReadByte(in);
   c_logmbox = File_ReadByte(in);
   c_svdmbox = File_ReadByte(in);
   c_binstrip = File_ReadByte(in);
   c_hdrstrip = File_ReadByte(in);
   c_sigstrip = File_ReadByte(in);
   c_shorthdr = File_ReadByte(in);
   c_p_noreject = File_ReadByte(in);
   c_p_ignoreua = File_ReadByte(in);
   c_p_preserve = File_ReadByte(in);
   c_p_norecog = File_ReadByte(in);
   c_p_autppre = File_ReadByte(in);
   File_Close(in);

   return;
}



static int  is_binary_start(char *line)
{
   /* Look for:
        "begin <number><number><number>"
      or
        "Content-Transfer-Encoding:"
   */
   char *marker;

   /* Is there a 'begin' present? */
   marker = strstr(line, "begin");
   if (marker != NULL)
   {
      /* If offset is not 0, it is not a UUcode */
      if ((marker - line) != 0) return FALSE;

      /* UUcode? Let's see if next character is a space, followed by three digits */
      if (line[5] != ' ') return FALSE;
      if (isdigit(line[6]) == FALSE) return FALSE;
      if (isdigit(line[7]) == FALSE) return FALSE;
      if (isdigit(line[8]) == FALSE) return FALSE;

      /* I guess it is a UUcode! */
      return TRUE;
   }

   /* Is there a 'Content-Transfer-Encoding: BASE64' present?
      We have to watch out for "Quoted-Printable" type headers... */
   marker = strstr(line, "Content-Transfer-Encoding: BASE64");
   if (marker != NULL)
   {
      /* If offset is not 0, it is not valid... */
      if ((marker - line) != 0) return FALSE;

      /* ...otherwise assume MIME start (text portion has 'content-type' header) */
      return TRUE;
   }

   /* MIME header, in lowercase (some routines use lowercase), this will become
      default when *all* scanning is case insensitive */
   marker = strstr(line, "content-transfer-encoding: base64");
   if (marker != NULL)
   {
      /* If offset is not 0, it is not valid... */
      if ((marker - line) != 0) return FALSE;

      /* ...otherwise assume MIME start (text portion has 'content-type' header) */
      return TRUE;
   }

   return FALSE;
}



static int  is_binary_end(char *line)
{
   char *marker;

   /* Look for 'end' */
   marker = strstr(line, "end");
   if (marker != NULL)
   {
      /* If offset is not 0, it is not a UUcode */
      if ((marker - line) != 0) return FALSE;
      return TRUE;
   }

   /* No? Okay, look for '--' is first two characters, would mark end of MIME. */
   if (line[0] == '-' && line[1] == '-') return TRUE;

   return FALSE;
}



static void lowercase(char string[])
{
   int i=0;

   while ( string[i] )
   {
      string[i] = tolower(string[i]); /* can't use "value or 32", not accented-letter-safe */
      i++;
   }

   return;
}



static void signl_generic(int sig)
{
   /* Trap errors */
   longjmp(posible_travectoria, sig);
}



static void signl_oserr(int sig)
{
   /* Trap undefined OS errors */
   _kernel_oserror *last_error;
   char *errmsg = "Error condition unknown";
   sig = sig;
   last_error = /*(_kernel_oserror*)*/ _kernel_last_oserror();
   if (last_error) errmsg = last_error->errmess;
   crash_msg = errmsg;
   longjmp(posible_travectoria, SIGOSERROR);
}



static void bbc_colour(int colour)
{
   /* Sets text colour in the Blue Screen Of Death
      (easier than typing this code each time...)
   */
   r.r[0] = 17;
   _kernel_swi(SWI_OS_WriteC, &r, &r);
   r.r[0] = colour;
   _kernel_swi(SWI_OS_WriteC, &r, &r);

   return;
}



static void bbc_tab(int cursorx, int cursory)
{
   /* Places the text cursor at the location specified */

   r.r[0] = 31;
   _kernel_swi(SWI_OS_WriteC, &r, &r);
   r.r[0] = cursorx;
   _kernel_swi(SWI_OS_WriteC, &r, &r);
   r.r[0] = cursory;
   _kernel_swi(SWI_OS_WriteC, &r, &r);

   return;
}



static void panic(int type)
{
   /* Draw the "Blue Screen of Death" and deal with error.
      Whee-hee! BSOD on RISC OS! I'm a sick **** aren't I? */
   int origmode = 0;

   /* Get original screen mode */
   r.r[0] = 0x87;
   _kernel_swi(SWI_OS_Byte, &r, &r);
   origmode = r.r[2];

   /* Change to mode 12 */
   r.r[0] = 22;
   _kernel_swi(SWI_OS_WriteC, &r, &r);
   r.r[0] = 12;
   _kernel_swi(SWI_OS_WriteC, &r, &r);
   bbc_colour(128 + 4);
   r.r[0] = 12;
   _kernel_swi(SWI_OS_WriteC, &r, &r);

   /* Print heading */
   bbc_colour(128 + 7);
   bbc_colour(4);
   bbc_tab(14, 8);
   printf(" A non-recoverable internal error has been detected \n\n");
   bbc_colour(128 + 4);

   /* Print error report */
   bbc_colour(7);
   switch (type)
   {
      case SIGABRT    : printf("   Error type        : SIGABRT (abort)\n");
                        break;
      case SIGFPE     : printf("   Error type        : SIGFPE (arithmetic exception)\n");
      	   	      	break;
      case SIGILL     : printf("   Error type        : SIGILL (illegal instruction)\n");
      	   	      	break;
      case SIGINT     : printf("   Error type        : SIGINT (interrupt request)\n");
      	   	      	break;
      case SIGSEGV    : printf("   Error type        : SIGSEGV (bad memory access)\n");
      	   	      	break;
      case SIGTERM    : printf("   Error type        : SIGTERM (termination request)\n");
      	   	      	break;
      case SIGSTAK    : printf("   Error type        : SIGSTAK (stack overflow)\n");
                        printf("                       Increasing the WimpSlot maximum\n");
                        printf("                       might cure this problem.\n");
      	   	      	break;
      case SIGOSERROR : printf("   Error type        : SIGOSERROR (OS error)\n");
                        printf("   Error message     : %s\n", crash_msg);
                        break;
      default         : printf("   Error type        : Unrecognised (code %d)\n", type);
      		      	break;
   }

   /* Dump core */
   r.r[0] = -1;
   r.r[1] = -1;
   _kernel_swi(0x400EC, &r, &r); /* Wimp_SlotSize */
   junk_v = r.r[0];
   printf("\n\n\n");
   printf("   Dumping core (&8000 to &%d (%dK) to '!COREDUMP!', please wait...", junk_v, ((junk_v - 0x8000)/1024));
   coredump(type);

   /* Restore original screen mode, disable command window and exit */
   Wimp_CommandWindow(-1);
   Wimp_SetMode(origmode);
   exit(EXIT_FAILURE);
}



static void coredump(int type)
{
   /* This coredumps... */
   FILE *f;
   int current;

   Hourglass_On();

   /* Open up the !COREDUMP! file */
   remove("<FetchMail$Dir>.!COREDUMP!");
   f = fopen("<FetchMail$Dir>.!COREDUMP!", "wb");
   if (!f) return;
   /* setbuf(f, NULL);   << don't disable buffering, for speed */

   /* Report error type */
   switch (type)
   {
      case SIGABRT    : fprintf(f, "   Err : SIGABRT\n");
                        break;
      case SIGFPE     : fprintf(f, "   Err : SIGFPE\n");
      	   	      	break;
      case SIGILL     : fprintf(f, "   Err : SIGILL\n");
      	   	      	break;
      case SIGINT     : fprintf(f, "   Err : SIGINT\n");
      	   	      	break;
      case SIGSEGV    : fprintf(f, "   Err : SIGSEGV\n");
      	   	      	break;
      case SIGTERM    : fprintf(f, "   Err : SIGTERM\n");
      	   	      	break;
      case SIGSTAK    : fprintf(f, "   Err : SIGSTAK\n");
      	   	      	break;
      case SIGOSERROR : fprintf(f, "   Err : SIGOSERROR\n");
                        fprintf(f, "   Msg : %s\n", crash_msg);
                        break;
      default         : fprintf(f, "   Err : ? (code %d)\n", type);
      		      	break;
   }

   /* Get/report application slot size */
   r.r[0] = -1;
   r.r[1] = -1;
   _kernel_swi(0x400EC, &r, &r); /* Wimp_SlotSize */
   current = r.r[0];
   fprintf(f, "\n   Application slot size is %d (%dK)\n\n\n", (current - 0x8000), ((current - 0x8000)/1024));

   /* Report locations of important functions for later pokeage */
   fprintf(f, "   main() . . . . . . . . &%08X\n", (int)&main);
   fprintf(f, "   panic()  . . . . . . . &%08X\n\n", (int)&panic);

   /* And important variables - same excuse */
   fprintf(f, "   posible_travectoria  . &%08X (at &%08X)\n\n", (int)posible_travectoria, (int)&posible_travectoria);
   fprintf(f, "   _kernel_swi_regs r . . (at &%08X)\n", (int)&r);
   fprintf(f, "   struct *userdata . . . (at &%08X)\n", (int)&userdata);

   fprintf(f, "   This version compiled at %s on %s...\n", __TIME__, __DATE__);

   fprintf(f, "\n\n   Core dump follows:\n\n");
   fwrite((void *)0x8000, current, 1, f);
   fclose(f);

   Hourglass_Smash();

   return;
}



static void trace(char *message, ...)
{
   /* Trace file, accepts printf() style parameters */
   FILE *fp;
   int  items;
   char *out;
   va_list variables;
   struct tm *timepointer;
   time_t now;
   char timevar[40];

   if ((out = (char *)malloc(1024)) == NULL) /* Allocate a block of memory */
      return;

   /* Create a formatted string from the information given */
   va_start(variables, message);
   items = vsprintf(out, message, variables);

   /* Get the time and create a time string in the form: "DDMMYYHHMMSS" */
   time(&now);
   timepointer = localtime(&now);
   strftime(timevar, 40, "%d%m%Y%H%M%S", timepointer);

   /* Open up the trace file, move to end, write string. */
   remove("<FetchMail$Dir>.trace");
   fp = fopen("<FetchMail$Dir>.trace", "a");
   if (!fp) return;

   fprintf(fp, "%s : %s\n", timevar, out);
   fclose(fp);

   return;
}



static void build_user_menu(void)
{
   /* Creates the 'User list' menu */

   junk_s[0] = '\0';

   for (junk_v = 0; junk_v <= maximum_users; junk_v++)
   {
      if (junk_v != 0) strcat(junk_s, ",");

      strcat(junk_s, userdata[junk_v].realname);
   }

   usermenu = Menu_New("Users", junk_s);
   Event_Claim(event_MENU, event_ANY, event_ANY, menu_choice, NULL);

   return;
}



static void export_file(void)
{
   /* Deals with saving of files dragged to our icon. */
   char filename[256];
   char line[85];
   int  filelen = 0;

   /* Ascertain filename */
   Icon_GetText(saveas_window, 1, filename);

   out = File_Open(filename, file_WRITE);
   if (out == NULL)
   {
      Error_Report(1, "Unable to open the output file, does another program have it open?");
      return;
   }

   in = File_Open(which_savepath, file_READ); /* We've borrowed 'which_savepath' */
   if (in == NULL)
   {
      Error_Report(1, "Unable to open the input mail file, does it exist?");
      File_Close(out);
      remove(filename);
      return;
   }

   Hourglass_On();

   filelen = File_ReadExtent(in);

   if (filelen == 0)
   {
      Error_Report(1, "The input mail file is empty!");
      File_Close(in);
      File_Close(out);
      return;
   }

   do
   {
      /* Current byte by line conversion with no intelligence. Slow? Nope. BASIC is though.
         Just proves interpreted languages can take a flying f...<ahem!> when it comes to
         fast-executing repetitive stuff (like this).
      */

      /* Does a %ge hourglass so the user knows we haven't crashed - this has enough
         everheads as it is... */
      junk_v = ((int)File_ReturnPos(in) * 100) / filelen;
      Hourglass_Percentage(junk_v);

      /* Get the line, write the line */
      r.r[0] = 1;
      _kernel_swi(0x406C5, &r, &r); /* Hourglass_LEDs */
      junk_v = read_coded_line(in, line);
      r.r[0] = 2;
      _kernel_swi(0x406C5, &r, &r); /* Hourglass_LEDs */
      File_printf(out, "%s\n", line);
   } while (junk_v >= 0);

   /* A little ID :-) */
   File_printf(out, "Message file created by FetchMail v%s.", pversion);

   File_Close(in);
   File_Close(out);

   /* SetType so the more braindead systems treat it properly... Also helps get !Zap
      open it as text and not a byte dump. :-) */
   sprintf(junk_s, "%%SetType %s Text", filename);
   r.r[0] = (int)junk_s;
   _kernel_swi(SWI_OS_CLI, &r, &r);

   Hourglass_Off();

   return;
}



static BOOL dataloader(event_pollblock *event, void *reference)
{
   /* Loads a file dragged to us. */

   /* Copy input file name - we borrow 'which_savepath'. */
   strcpy(which_savepath, event->data.message.data.dataload.filename);

   /* ACK the DATALOAD message. */
   event->data.message.header.yourref = event->data.message.header.myref;
   event->data.message.header.action = message_DATALOADACK;
   Wimp_SendMessage(event_USERMESSAGE, &event->data.message, event->data.message.header.sender, 0);

   /* Open the saveas window */
   Window_Show(saveas_window, open_CENTERED);

   return TRUE;
}
