diff -rNu dillo/src/IO/mime.c dillo-external/src/IO/mime.c
--- dillo/src/IO/mime.c	2003-03-31 16:12:03.000000000 -0600
+++ dillo-external/src/IO/mime.c	2003-04-22 16:36:07.000000000 -0500
@@ -11,7 +11,7 @@
 
 #include "mime.h"
 #include "../list.h"
-
+#include "../external.h"
 
 typedef struct {
    const char *Name;   /* MIME type name */
@@ -44,7 +44,7 @@
  * 'Key' is the content-type string that identifies the MIME type
  * 'Method' is the function that handles it
  */
-static gint Mime_add_minor_type(const char *Key, Viewer_t Method)
+gint a_Mime_add_minor_type(const char *Key, Viewer_t Method)
 {
    a_List_add(MimeMinItems, MimeMinItemsSize, MimeMinItemsMax);
    MimeMinItems[MimeMinItemsSize].Name = Key;
@@ -58,7 +58,7 @@
  * 'Key' is the content-type string that identifies the MIME type
  * 'Method' is the function that handles it
  */
-static gint Mime_add_major_type(const char *Key, Viewer_t Method)
+gint a_Mime_add_major_type(const char *Key, Viewer_t Method)
 {
    a_List_add(MimeMajItems, MimeMajItemsSize, MimeMajItemsMax);
    MimeMajItems[MimeMajItemsSize].Name = Key;
@@ -101,20 +101,23 @@
  */
 void a_Mime_init()
 {
-   Mime_add_minor_type("image/gif", a_Gif_image);
+   a_Mime_add_minor_type("image/gif", a_Gif_image);
 #ifdef HAVE_LIBJPEG
-   Mime_add_minor_type("image/jpeg", a_Jpeg_image);
-   Mime_add_minor_type("image/pjpeg", a_Jpeg_image);
-   Mime_add_minor_type("image/jpg", a_Jpeg_image);
+   a_Mime_add_minor_type("image/jpeg", a_Jpeg_image);
+   a_Mime_add_minor_type("image/pjpeg", a_Jpeg_image);
+   a_Mime_add_minor_type("image/jpg", a_Jpeg_image);
 #endif
 #ifdef HAVE_LIBPNG
-   Mime_add_minor_type("image/png", a_Png_image);
-   Mime_add_minor_type("image/x-png", a_Png_image);    /* deprecated */
+   a_Mime_add_minor_type("image/png", a_Png_image);
+   a_Mime_add_minor_type("image/x-png", a_Png_image);    /* deprecated */
 #endif
-   Mime_add_minor_type("text/html", a_Html_text);
+   a_Mime_add_minor_type("text/html", a_Html_text);
 
    /* Add a major type to handle all the text stuff */
-   Mime_add_major_type("text", a_Plain_text);
+   a_Mime_add_major_type("text", a_Plain_text);
+
+   /* Add all external viewers */
+   a_External_set_viewers();
 }
 
 
diff -rNu dillo/src/IO/mime.h dillo-external/src/IO/mime.h
--- dillo/src/IO/mime.h	2003-03-31 16:12:03.000000000 -0600
+++ dillo-external/src/IO/mime.h	2003-04-22 16:30:55.000000000 -0500
@@ -32,6 +32,9 @@
 #endif
 DwWidget* a_Gif_image (const char *Type,void *web, CA_Callback_t *Call,
                        void **Data);
+DwWidget* a_Extern_viewer (const char *Type, void *web, CA_Callback_t *Call,
+			   void **Data);
+void a_External_set_viewers();
 
 /*
  * Functions defined inside Mime module
@@ -39,5 +42,8 @@
 void a_Mime_init(void);
 DwWidget* a_Mime_set_viewer(const char *content_type, void *Ptr,
                             CA_Callback_t *Call, void **Data);
+gint a_Mime_add_minor_type(const char *Key, Viewer_t Method);
+gint a_Mime_add_major_type(const char *Key, Viewer_t Method);
+
 
 #endif /* __MIME_H__ */
diff -rNu dillo/src/Makefile.am dillo-external/src/Makefile.am
--- dillo/src/Makefile.am	2003-04-03 10:48:15.000000000 -0600
+++ dillo-external/src/Makefile.am	2003-04-22 16:30:55.000000000 -0500
@@ -107,6 +107,8 @@
 	cookies.c \
 	cookies.h \
 	list.h \
-	pixmaps.h
+	pixmaps.h \
+	external.c \
+	external.h
 
 EXTRA_DIST = chg srch
diff -rNu dillo/src/commands.c dillo-external/src/commands.c
--- dillo/src/commands.c	2003-04-15 14:24:38.000000000 -0500
+++ dillo-external/src/commands.c	2003-04-22 16:30:55.000000000 -0500
@@ -25,6 +25,7 @@
 #include "prefs.h"
 #include "menu.h"
 #include "capi.h"
+#include "external.h"
 
 /*
  * Local data
@@ -65,10 +66,21 @@
 }
 
 /*
- * ?
+ * Callback invoked when the prefs are done being edited.
+ * Note:  If prefs contents are stored outside the prefs structure
+ *        anywhere, they need to be updated.
+ */
+static void Commands_prefs_done_callback(int status, void *data) {
+  a_Prefs_init();
+}
+
+/*
+ * You will need to export (or setenv) EDITOR for this to work.
  */
 void a_Commands_prefs_callback(GtkWidget *widget, gpointer client_data)
 {
+  a_External_make_process("xterm -e $EDITOR ~/.dillo/dillorc", 
+			  Commands_prefs_done_callback, NULL);
 }
 
 /*
diff -rNu dillo/src/external.c dillo-external/src/external.c
--- dillo/src/external.c	1969-12-31 18:00:00.000000000 -0600
+++ dillo-external/src/external.c	2003-04-22 16:30:55.000000000 -0500
@@ -0,0 +1,372 @@
+/*
+ * File: external.c
+ *
+ * Copyright (C) 2001 Lars Clausen (lrclause@cs.uiuc.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+/* Functions for handling calling external programs (in particular,
+ * external mime handlers and stand-alone programs).
+ */
+
+#include <string.h>
+#include <gtk/gtk.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <glib.h>
+#include <fcntl.h>
+
+#include "dillo.h"
+#include "dw.h"
+#include "IO/mime.h"
+#include "debug.h"
+#include "list.h"
+#include "interface.h"
+#include "progressbar.h"
+
+/* I'm loath to hack mime.c to add callback data for this, since I don't
+   understand where the CBData comes from.  Instead I (for now) hold a list
+   here of the commands.  Not fast, but hey, neither is spawning an
+   external viewer.
+*/
+
+typedef struct {
+   char *mime_type;
+   char *command;
+} Dillo_External_Mime_Handler_t;
+
+typedef struct {
+   int offset;
+   int temp_fd;
+   char *temp_file_name;
+   BrowserWindow *bw;
+   char *command;
+} Dillo_External_Writer_t;
+
+typedef void (External_Callback_t)(int retval, 
+				    void *callback_data);
+
+typedef struct {
+  int pid;
+  External_Callback_t *callback;
+  void *callback_data;
+} Dillo_External_Process_t;
+
+
+gint ExternalViewersSize = 0, ExternalViewersMax = 8;
+Dillo_External_Mime_Handler_t *ExternalViewers = NULL;
+gint ExternalProgramsSize = 0, ExternalProgramsMax = 8;
+Dillo_External_Process_t *ExternalPrograms = NULL;
+
+gboolean a_External_make_process(char *Command,
+				 External_Callback_t *CB,
+				 void *CBData);
+static void External_reap_process(int signum);
+static void External_viewer_callback(int Op, CacheClient_t *Client);
+static Dillo_External_Writer_t *External_viewer_new(BrowserWindow *bw,
+						    char *command);
+static void External_viewer_reap_callback(int status, 
+					  void *data);
+static char *External_viewer_find_command(const char *Type);
+static DwWidget *External_viewer(const char *Type, void *Ptr,
+				 CA_Callback_t *Call, void **Data);
+static void External_set_viewers_from_file(char *filename);
+void a_External_set_viewers();
+
+/*
+ * Start an external process using system(), calling the callback function
+ * when the process dies.  Returns TRUE if the fork was successfull.
+ */
+gboolean 
+a_External_make_process(char *Command, External_Callback_t *CB, void *CBData) {
+   /*  va_list args;*/
+   int pid;
+
+   pid = fork();
+
+   if (pid < 0) {
+      DEBUG_MSG(10, "Error starting external program %s\n", Command);
+      return FALSE;
+   }
+   if (pid == 0) {
+      /*    va_start(args, command);*/
+      int retval;
+
+      signal(SIGCHLD, _exit);
+
+      retval = system(Command);
+      if (retval == -1) 
+	 DEBUG_MSG(10, "Error executing external program %s: %s\n", 
+		   Command, strerror(errno));
+      DEBUG_MSG(3, "Returned from %s, return value=%d\n", Command, retval);
+      exit(retval);
+   } else {
+     signal(SIGCHLD, External_reap_process);
+      DEBUG_MSG(1, "New external PID is %d\n", pid);
+      a_List_add(ExternalPrograms, ExternalProgramsSize, 
+		 ExternalProgramsMax);
+      ExternalPrograms[ExternalProgramsSize].callback = CB;
+      ExternalPrograms[ExternalProgramsSize].callback_data = CBData;
+      ExternalPrograms[ExternalProgramsSize].pid = pid;
+      ExternalProgramsSize++;
+
+      return FALSE;
+   }
+}
+
+/* 
+ * Signal handler: Reaps dead children of external processes.
+ * Since internal processes never die (they are just reused), we only
+ * need to do this for external processes, which should all be created
+ * through a_External_make_process().
+ */
+static void 
+External_reap_process(int signum) {
+  int status;
+  int dead_pid;
+  int i;
+
+  for (i = 0; i < ExternalProgramsSize; i++) {
+    if ((dead_pid = waitpid(ExternalPrograms[i].pid, &status, WNOHANG)) > 0) {
+      DEBUG_MSG(1, "Reaping child %d\n", dead_pid);
+      if (ExternalPrograms[i].callback != NULL) {
+	(*ExternalPrograms[i].callback)(status,
+				       ExternalPrograms[i].callback_data);
+      }
+      a_List_remove(ExternalPrograms, i, ExternalProgramsSize);
+      return;
+    }
+  }
+}
+
+/*
+ * Below here are the more specific external MIME viewers.
+ */
+
+/*
+ * The callback function for external MIME viewers as cache clients.
+ */
+static void 
+External_viewer_callback(int Op, CacheClient_t *Client) {
+   Dillo_External_Writer_t *external = Client->CbData;
+   int bufsize = Client->BufSize;
+
+   if ( Op != CA_Send) {
+     char *commandbuf;
+     /* Close or Abort */
+    
+      /* Do the last line: */
+      if (external->offset < bufsize) {
+	 write(external->temp_fd, Client->Buf+external->offset, bufsize-external->offset);
+	 external->offset = bufsize;
+      }
+      /* remove this client from our active list */
+      a_Interface_close_client(external->bw, Client->Key);
+      /* set progress bar insensitive */
+      a_Progressbar_update(external->bw->progress, NULL, 0);
+
+      /* Start a new process and remember its filename */
+      commandbuf = g_new(char, strlen(external->command)+
+			 strlen(external->temp_file_name));
+      sprintf(commandbuf, external->command, 
+	      external->temp_file_name);
+      a_External_make_process(commandbuf, External_viewer_reap_callback,
+			      external->temp_file_name);
+      
+      g_free(commandbuf);
+      close(external->temp_fd);
+      g_free(external);
+   } else {
+      int written;
+      written = write(external->temp_fd, Client->Buf+external->offset, bufsize-external->offset);
+      external->offset = bufsize;
+   }
+}
+
+/*
+ * Creates a new cache client structure, including a temporary file
+ * for downloading.
+ */
+static Dillo_External_Writer_t *
+External_viewer_new(BrowserWindow *bw, char *command) {
+   Dillo_External_Writer_t *external;
+   char *tmp_name_buf = g_strdup("/tmp/dillo_XXXXXX");
+
+   external = g_new(Dillo_External_Writer_t, 1);
+   external->command = command;
+   /* Have to use mkstemp rather than tmpfile, since we want to get at the
+     filename afterwards to give to the external app.
+  */
+   external->temp_fd = mkstemp(tmp_name_buf);
+   external->temp_file_name = tmp_name_buf;
+   external->bw = bw;
+   external->offset = 0;
+
+   return external;
+}
+
+/*
+ * The callback for when an external viewer exits.  Removes the temporary
+ * file.
+ * We might want to have a function to remove them in the case where Dillo
+ * dies before its children.
+ */
+static void 
+External_viewer_reap_callback(int status, void *data) {
+  char *filename = (char *)data;
+
+  if (filename != NULL) {
+    unlink(filename);
+    g_free(filename);
+  }
+}
+
+/*
+ * Finds the command corresponding to a particular MIME type.
+ * This function is unnecessary is mime.c allows us to use callback data.
+ */
+static char *
+External_viewer_find_command(const char *Type) {
+  int i;
+  char *slashindex;
+  char *major_type;
+  int major_len;
+
+  for (i = 0; i < ExternalViewersSize; i++) {
+    if (strcmp(ExternalViewers[i].mime_type, Type) == 0)
+      return ExternalViewers[i].command;
+  }
+
+  slashindex = strchr(Type, '/');
+  if (slashindex != NULL)
+    major_type = g_strndup(Type, slashindex-Type);
+  else 
+    major_type = g_strdup(Type);
+  major_len = strlen(major_type);
+
+  for (i = 0; i < ExternalViewersSize; i++) {
+    if (strncmp(ExternalViewers[i].mime_type, major_type, major_len) == 0) {
+      g_free(major_type);
+      return ExternalViewers[i].command;
+    }
+  }
+  g_free(major_type);
+  return NULL;
+}
+
+/*
+ * Callback function for a mime type.
+ */
+static DwWidget *
+External_viewer(const char *Type, void *Ptr,
+		CA_Callback_t *Call, void **Data) {
+   char *command = External_viewer_find_command(Type);
+   DilloWeb *Web = (DilloWeb*)Ptr;
+
+   Dillo_External_Writer_t *ext;
+
+   DEBUG_MSG(1, "Found command %s for %s\n", command, Type);
+
+   if (command == NULL) {
+      DEBUG_MSG(3, "Not really a viewer for '%s' after all?\n", Type);
+      return NULL;
+   }
+
+   /* Get the data into a temporary file */
+   ext = External_viewer_new(Web->bw, command);
+
+   *Call = (CA_Callback_t)External_viewer_callback;
+   *Data = (void *)ext;
+
+   /* Always return NULL, as these don't use the Dillo window */
+   DEBUG_MSG(5, "Starting external viewer %s for type %s\n", command, Type);
+   return NULL;
+}
+
+/*
+ * Adds an external viewer for a particular mime type.
+ */
+static void 
+External_viewer_add(const char *Type, const char *command) {
+   DEBUG_MSG(1, "Adding viewer for `%s': %s\n", Type, command);
+   if (strchr(Type, '/') != NULL) {
+      a_Mime_add_minor_type(Type, External_viewer);
+   } else {
+      a_Mime_add_major_type(Type, External_viewer);
+   }
+   a_List_add(ExternalViewers, ExternalViewersSize,
+	      ExternalViewersMax);
+   ExternalViewers[ExternalViewersSize].mime_type = g_strdup(Type);
+   ExternalViewers[ExternalViewersSize].command = g_strdup(command);
+   ExternalViewersSize++;
+}
+
+/*
+ * This is a very simple mailcap parser, ignores all print, test etc
+ * and other remarks.
+ */
+static void 
+External_set_viewers_from_file(char *filename) {
+   char *buf;
+   gchar **lines;
+   int i;
+   int alloclen = 4000;
+   int bytes_read = 0;
+   int bytes_read_now;
+   gint fd;
+  
+   fd = open (filename, O_RDONLY);
+  
+   if (fd < 0) return;
+  
+   buf = g_new(char, alloclen);
+   /* Snarf the file */
+   while ((bytes_read_now = read(fd, buf+bytes_read, alloclen-bytes_read)) > 0) {
+      bytes_read += bytes_read_now;
+      if (bytes_read == alloclen) {
+         alloclen *= 2;
+         buf = g_realloc(buf, alloclen*sizeof(char));
+      }
+   }
+   close(fd);
+
+   lines = g_strsplit(buf, "\n", 0);
+   
+   for (i = 0; lines[i] != NULL; i++) {
+     char *semi1, *semi2;
+     if (*lines[i] == '\0' || *lines[i] == '#') continue;
+     semi1 = strchr(lines[i], ';');
+     if (semi1 == NULL) continue;
+     *semi1 = '\0';
+     semi1++;
+     semi2 = strchr(semi1, ';');
+     if (semi2 != NULL) *semi2 = '\0';
+     semi1 = g_strchomp(semi1);
+     External_viewer_add(g_strdup(lines[i]), g_strdup(semi1));
+   }
+   g_strfreev(lines);
+   g_free(buf);
+
+}
+
+/* Sets up external viewers taken from the users mailcap, then from
+ * the system mailcap.  Since mime viewers are checked in order,
+ * the users settings have preference (but this is called after Dillos
+ * own viewers are checked).
+ */
+void 
+a_External_set_viewers() {
+   External_set_viewers_from_file("/home/lrclause/.mailcap");
+   External_set_viewers_from_file("/etc/mailcap");
+}
diff -rNu dillo/src/external.h dillo-external/src/external.h
--- dillo/src/external.h	1969-12-31 18:00:00.000000000 -0600
+++ dillo-external/src/external.h	2003-04-22 16:30:55.000000000 -0500
@@ -0,0 +1,13 @@
+#ifndef __DILLO_EXTERNAL_H__
+#define __DILLO_EXTERNAL_H__
+
+typedef void (External_Callback_t)(int retval, 
+				   void *callback_data);
+
+extern gboolean a_External_make_process(char *Command,
+					External_Callback_t *CB,
+					void *CBData);
+extern void a_External_set_viewers();
+
+
+#endif
