one more fix to expand_vars
[uzbl-mobile] / uzbl.c
diff --git a/uzbl.c b/uzbl.c
index c784085..ee5c6bb 100644 (file)
--- a/uzbl.c
+++ b/uzbl.c
@@ -41,6 +41,7 @@
 #include <sys/types.h>
 #include <sys/un.h>
 #include <sys/utsname.h>
+#include <sys/time.h>
 #include <webkit/webkit.h>
 #include <stdio.h>
 #include <string.h>
 #include <libsoup/soup.h>
 #include <signal.h>
 #include "uzbl.h"
-
+#include "config.h"
 
 static Uzbl uzbl;
+typedef void (*Command)(WebKitWebView*, GArray *argv);
+
+
+
+/* commandline arguments (set initial values for the state variables) */
+static const 
+GOptionEntry entries[] =
+{
+    { "uri",     'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
+        "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
+    { "verbose", 'v', 0, G_OPTION_ARG_NONE,   &uzbl.state.verbose,
+        "Whether to print all messages or just errors.", NULL },
+    { "name",    'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name, 
+        "Name of the current instance (defaults to Xorg window id)", "NAME" },
+    { "config",  'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,   
+        "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
+    { NULL,      0, 0, 0, NULL, NULL, NULL }
+};
+
+/* associate command names to their properties */
+typedef const struct {
+    void **ptr;
+    int type;
+    int dump;
+    void (*func)(void);
+} uzbl_cmdprop;
+
+enum {TYPE_INT, TYPE_STR};
+
+/* an abbreviation to help keep the table's width humane */
+#define PTR(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .func = fun }
 
-/* define names and pointers to all config specific variables */
 const struct {
     char *name;
-    void **ptr;
+    uzbl_cmdprop cp;
 } var_name_to_ptr[] = {
-    { "uri",                (void *)&uzbl.state.uri                 },
-    { "status_message",     (void *)&uzbl.gui.sbar.msg              },
-    { "show_status",        (void *)&uzbl.behave.show_status        },
-    { "status_top",         (void *)&uzbl.behave.status_top         },
-    { "status_format",      (void *)&uzbl.behave.status_format      },
-    { "status_background",  (void *)&uzbl.behave.status_background  },
-    { "title_format_long",  (void *)&uzbl.behave.title_format_long  },
-    { "title_format_short", (void *)&uzbl.behave.title_format_short },
-    { "insert_mode",        (void *)&uzbl.behave.insert_mode        },
-    { "always_insert_mode", (void *)&uzbl.behave.always_insert_mode },
-    { "reset_command_mode", (void *)&uzbl.behave.reset_command_mode },
-    { "modkey"     ,        (void *)&uzbl.behave.modkey             },
-    { "load_finish_handler",(void *)&uzbl.behave.load_finish_handler},
-    { "load_start_handler", (void *)&uzbl.behave.load_start_handler },
-    { "load_commit_handler",(void *)&uzbl.behave.load_commit_handler},
-    { "history_handler",    (void *)&uzbl.behave.history_handler    },
-    { "download_handler",   (void *)&uzbl.behave.download_handler   },
-    { "cookie_handler",     (void *)&uzbl.behave.cookie_handler     },
-    { "fifo_dir",           (void *)&uzbl.behave.fifo_dir           },
-    { "socket_dir",         (void *)&uzbl.behave.socket_dir         },
-    { "http_debug",         (void *)&uzbl.behave.http_debug         },
-    { "default_font_size",  (void *)&uzbl.behave.default_font_size  },
-    { "minimum_font_size",  (void *)&uzbl.behave.minimum_font_size  },
-    { "shell_cmd",          (void *)&uzbl.behave.shell_cmd          },
-    { "proxy_url",          (void *)&uzbl.net.proxy_url             },
-    { "max_conns",          (void *)&uzbl.net.max_conns             },
-    { "max_conns_host",     (void *)&uzbl.net.max_conns_host        },
-    { "useragent",          (void *)&uzbl.net.useragent             },
-    { NULL,                 NULL                                    }
+/*    variable name         pointer to variable in code          type  dump callback function    */
+/*  --------------------------------------------------------------------------------------- */
+    { "uri",                 PTR(uzbl.state.uri,                  STR,  1,   cmd_load_uri)},
+    { "mode",                PTR(uzbl.behave.mode,                INT,  0,   NULL)},
+    { "inject_html",         PTR(uzbl.behave.inject_html,         STR,  0,   cmd_inject_html)},
+    { "base_url",            PTR(uzbl.behave.base_url,            STR,  1,   NULL)},
+    { "html_endmarker",      PTR(uzbl.behave.html_endmarker,      STR,  1,   NULL)},
+    { "html_mode_timeout",   PTR(uzbl.behave.html_timeout,        INT,  1,   NULL)},
+    { "status_message",      PTR(uzbl.gui.sbar.msg,               STR,  1,   update_title)},
+    { "show_status",         PTR(uzbl.behave.show_status,         INT,  1,   cmd_set_status)},
+    { "status_top",          PTR(uzbl.behave.status_top,          INT,  1,   move_statusbar)},
+    { "status_format",       PTR(uzbl.behave.status_format,       STR,  1,   update_title)},
+    { "status_pbar_done",    PTR(uzbl.gui.sbar.progress_s,        STR,  1,   update_title)},
+    { "status_pbar_pending", PTR(uzbl.gui.sbar.progress_u,        STR,  1,   update_title)},
+    { "status_pbar_width",   PTR(uzbl.gui.sbar.progress_w,        INT,  1,   update_title)},
+    { "status_background",   PTR(uzbl.behave.status_background,   STR,  1,   update_title)},
+    { "insert_indicator",    PTR(uzbl.behave.insert_indicator,    STR,  1,   update_title)},
+    { "command_indicator",   PTR(uzbl.behave.cmd_indicator,       STR,  1,   update_title)},
+    { "title_format_long",   PTR(uzbl.behave.title_format_long,   STR,  1,   update_title)},
+    { "title_format_short",  PTR(uzbl.behave.title_format_short,  STR,  1,   update_title)},
+    { "insert_mode",         PTR(uzbl.behave.insert_mode,         INT,  1,   NULL)},
+    { "always_insert_mode",  PTR(uzbl.behave.always_insert_mode,  INT,  1,   cmd_always_insert_mode)},
+    { "reset_command_mode",  PTR(uzbl.behave.reset_command_mode,  INT,  1,   NULL)},
+    { "modkey",              PTR(uzbl.behave.modkey,              STR,  1,   cmd_modkey)},
+    { "load_finish_handler", PTR(uzbl.behave.load_finish_handler, STR,  1,   NULL)},
+    { "load_start_handler",  PTR(uzbl.behave.load_start_handler,  STR,  1,   NULL)},
+    { "load_commit_handler", PTR(uzbl.behave.load_commit_handler, STR,  1,   NULL)},
+    { "history_handler",     PTR(uzbl.behave.history_handler,     STR,  1,   NULL)},
+    { "download_handler",    PTR(uzbl.behave.download_handler,    STR,  1,   NULL)},
+    { "cookie_handler",      PTR(uzbl.behave.cookie_handler,      STR,  1,   cmd_cookie_handler)},
+    { "fifo_dir",            PTR(uzbl.behave.fifo_dir,            STR,  1,   cmd_fifo_dir)},
+    { "socket_dir",          PTR(uzbl.behave.socket_dir,          STR,  1,   cmd_socket_dir)},
+    { "http_debug",          PTR(uzbl.behave.http_debug,          INT,  1,   cmd_http_debug)},
+    { "shell_cmd",           PTR(uzbl.behave.shell_cmd,           STR,  1,   NULL)},
+    { "proxy_url",           PTR(uzbl.net.proxy_url,              STR,  1,   set_proxy_url)},
+    { "max_conns",           PTR(uzbl.net.max_conns,              INT,  1,   cmd_max_conns)},
+    { "max_conns_host",      PTR(uzbl.net.max_conns_host,         INT,  1,   cmd_max_conns_host)},
+    { "useragent",           PTR(uzbl.net.useragent,              STR,  1,   cmd_useragent)},
+    /* exported WebKitWebSettings properties*/
+    { "font_size",           PTR(uzbl.behave.font_size,           INT,  1,   cmd_font_size)},
+    { "monospace_size",      PTR(uzbl.behave.monospace_size,      INT,  1,   cmd_font_size)},
+    { "minimum_font_size",   PTR(uzbl.behave.minimum_font_size,   INT,  1,   cmd_minimum_font_size)},
+    { "disable_plugins",     PTR(uzbl.behave.disable_plugins,     INT,  1,   cmd_disable_plugins)},
+    { "disable_scripts",     PTR(uzbl.behave.disable_scripts,     INT,  1,   cmd_disable_scripts)},
+    { "autoload_images",     PTR(uzbl.behave.autoload_img,        INT,  1,   cmd_autoload_img)},
+    { "autoshrink_images",   PTR(uzbl.behave.autoshrink_img,      INT,  1,   cmd_autoshrink_img)},
+    { "enable_spellcheck",   PTR(uzbl.behave.enable_spellcheck,   INT,  1,   cmd_enable_spellcheck)},
+    { "enable_private",      PTR(uzbl.behave.enable_private,      INT,  1,   cmd_enable_private)},
+    { "print_backgrounds",   PTR(uzbl.behave.print_bg,            INT,  1,   cmd_print_bg)},
+    { "stylesheet_uri",      PTR(uzbl.behave.style_uri,           STR,  1,   cmd_style_uri)},
+    { "resizable_text_areas",PTR(uzbl.behave.resizable_txt,       INT,  1,   cmd_resizable_txt)},
+    { "default_encoding",    PTR(uzbl.behave.default_encoding,    STR,  1,   cmd_default_encoding)},
+    { "enforce_96_dpi",      PTR(uzbl.behave.enforce_96dpi,       INT,  1,   cmd_enforce_96dpi)},
+    { "caret_browsing",      PTR(uzbl.behave.caret_browsing,      INT,  1,   cmd_caret_browsing)},
+
+    { NULL,                  {.ptr = NULL, .type = TYPE_INT, .dump = 0, .func = NULL}}
 }, *n2v_p = var_name_to_ptr;
 
 const struct {
@@ -117,29 +175,59 @@ const struct {
     { NULL,      0                }
 };
 
+
 /* construct a hash from the var_name_to_ptr array for quick access */
 static void
 make_var_to_name_hash() {
     uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
     while(n2v_p->name) {
-        g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, n2v_p->ptr);
+        g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
         n2v_p++;
     }
 }
 
-/* commandline arguments (set initial values for the state variables) */
-static GOptionEntry entries[] =
-{
-    { "uri",     'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,           "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
-    { "verbose", 'v', 0, G_OPTION_ARG_NONE,   &uzbl.state.verbose,       "Whether to print all messages or just errors.", NULL },
-    { "name",    'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name, "Name of the current instance (defaults to Xorg window id)", "NAME" },
-    { "config",  'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,   "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
-    { NULL,      0, 0, 0, NULL, NULL, NULL }
-};
-
-typedef void (*Command)(WebKitWebView*, GArray *argv);
-
 /* --- UTILITY FUNCTIONS --- */
+static gchar *
+expand_vars(char *s) {
+
+    char ret[256],  /* 256 chars per var name should be safe */
+         *vend;
+    uzbl_cmdprop *c;
+    GString *buf = g_string_new("");
+
+    while(*s) {
+        /* found quotation char */
+        if(*s == '\\') {
+            g_string_append_c(buf, *++s);
+            s++;
+        }
+        /* found variable */
+        else if(*s == '@') {
+            s++;
+            if( (vend = strchr(s, ' ')) ||
+                (vend = strchr(s, '\0')) ) {
+                strncpy(ret, s, vend-s);
+                ret[vend-s] = '\0';
+                if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
+                    if(c->type == TYPE_STR)
+                        g_string_append(buf, (gchar *)*c->ptr);
+                    else if(c->type == TYPE_INT) {
+                        char *b = itos((int)*c->ptr);
+                        g_string_append(buf, b);
+                        g_free(b);
+                    }
+                }
+                s = vend;
+            }
+        }
+        /* every other char */
+        else {
+            g_string_append_c(buf, *s);
+            s++;
+        }
+    }
+    return g_string_free(buf, FALSE);
+}
 
 char *
 itos(int val) {
@@ -150,27 +238,37 @@ itos(int val) {
 }
 
 static gchar*
+strfree(gchar *str) { g_free(str); return NULL; }  // for freeing & setting to null in one go
+
+static gchar*
 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
 
 static char *
 str_replace (const char* search, const char* replace, const char* string) {
-    return g_strjoinv (replace, g_strsplit (string, search, -1));
+    gchar **buf;
+    char *ret;
+
+    buf = g_strsplit (string, search, -1);
+    ret = g_strjoinv (replace, buf);
+    g_strfreev(buf); // somebody said this segfaults
+
+    return ret;
 }
 
-static gchar**
+static GArray*
 read_file_by_line (gchar *path) {
     GIOChannel *chan = NULL;
     gchar *readbuf = NULL;
     gsize len;
-    gchar *lines[512];
-    int i;
+    GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
+    int i = 0;
     
     chan = g_io_channel_new_file(path, "r", NULL);
     
     if (chan) {
-        while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL)
-               == G_IO_STATUS_NORMAL) {
-            lines[i] = g_strdup (readbuf);
+        while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
+            const gchar* val = g_strdup (readbuf);
+            g_array_append_val (lines, val);
             g_free (readbuf);
             i ++;
         }
@@ -180,31 +278,33 @@ read_file_by_line (gchar *path) {
         fprintf(stderr, "File '%s' not be read.\n", path);
     }
     
-    lines[i] = NULL;
     return lines;
 }
 
 static
-gchar* parseenv (const char* string) {
+gchar* parseenv (char* string) {
     extern char** environ;
-    gchar* newstring = g_strdup (string);
+    gchar* tmpstr = NULL;
     int i = 0;
+    
 
     while (environ[i] != NULL) {
-        gchar** env = g_strsplit (environ[i], "=", 0);
-        gchar* envname = malloc (strlen (env[0]) + 1);
-
-        strcat (envname, "$");
-        strcat (envname, env[0]);
-
-        newstring = str_replace(envname, env[1], newstring);
+        gchar** env = g_strsplit (environ[i], "=", 2);
+        gchar* envname = g_strconcat ("$", env[0], NULL);
+
+        if (g_strrstr (string, envname) != NULL) {
+            tmpstr = g_strdup(string);
+            g_free (string);
+            string = str_replace(envname, env[1], tmpstr);
+            g_free (tmpstr);
+        }
 
         g_free (envname);
-        //g_strfreev (env); - This still breaks uzbl, but shouldn't. The mystery thickens...
-        i ++;
+        g_strfreev (env); // somebody said this breaks uzbl
+        i++;
     }
 
-    return newstring;
+    return string;
 }
 
 static sigfunc*
@@ -228,11 +328,25 @@ clean_up(void) {
     if (uzbl.behave.socket_dir)
         unlink (uzbl.comm.socket_path);
 
+    g_free(uzbl.state.executable_path);
     g_string_free(uzbl.state.keycmd, TRUE);
     g_hash_table_destroy(uzbl.bindings);
     g_hash_table_destroy(uzbl.behave.commands);
 }
 
+/* used for html_mode_timeout 
+ * be sure to extend this function to use
+ * more timers if needed in other places
+*/
+static void
+set_timeout(int seconds) {
+    struct itimerval t;
+    memset(&t, 0, sizeof t);
+
+    t.it_value.tv_sec =  seconds;
+    t.it_value.tv_usec = 0;
+    setitimer(ITIMER_REAL, &t, NULL);
+}
 
 /* --- SIGNAL HANDLER --- */
 
@@ -249,6 +363,15 @@ catch_sigint(int s) {
     exit(EXIT_SUCCESS);
 }
 
+static void
+catch_alrm(int s) {
+    (void) s;
+
+    set_var_value("mode", "0");
+    render_html();
+}
+
+
 /* --- CALLBACKS --- */
 
 static gboolean
@@ -270,7 +393,7 @@ create_web_view_cb (WebKitWebView  *web_view, WebKitWebFrame *frame, gpointer us
     (void) web_view;
     (void) frame;
     (void) user_data;
-    if (uzbl.state.selected_url[0]!=0) {
+    if (uzbl.state.selected_url != NULL) {
         if (uzbl.state.verbose)
             printf("\nNew web view -> %s\n",uzbl.state.selected_url);
         new_window_load_uri(uzbl.state.selected_url);
@@ -306,23 +429,27 @@ scroll (GtkAdjustment* bar, GArray *argv) {
     gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
 }
 
-static void scroll_begin(WebKitWebView* page, GArray *argv) {
+static void
+scroll_begin(WebKitWebView* page, GArray *argv) {
     (void) page; (void) argv;
     gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
 }
 
-static void scroll_end(WebKitWebView* page, GArray *argv) {
+static void
+scroll_end(WebKitWebView* page, GArray *argv) {
     (void) page; (void) argv;
     gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
                               gtk_adjustment_get_page_size(uzbl.gui.bar_v));
 }
 
-static void scroll_vert(WebKitWebView* page, GArray *argv) {
+static void
+scroll_vert(WebKitWebView* page, GArray *argv) {
     (void) page;
     scroll(uzbl.gui.bar_v, argv);
 }
 
-static void scroll_horz(WebKitWebView* page, GArray *argv) {
+static void
+scroll_horz(WebKitWebView* page, GArray *argv) {
     (void) page;
     scroll(uzbl.gui.bar_h, argv);
 }
@@ -357,9 +484,10 @@ link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpoin
     (void) title;
     (void) data;
     //Set selected_url state variable
-    uzbl.state.selected_url[0] = '\0';
+    g_free(uzbl.state.selected_url);
+    uzbl.state.selected_url = NULL;
     if (link) {
-        strcpy (uzbl.state.selected_url, link);
+        uzbl.state.selected_url = g_strdup(link);
     }
     update_title();
 }
@@ -397,6 +525,7 @@ load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
     (void) page;
     (void) frame;
     (void) data;
+    g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
     if (uzbl.behave.load_start_handler)
         run_handler(uzbl.behave.load_start_handler, "");
 }
@@ -405,14 +534,13 @@ static void
 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
     (void) page;
     (void) data;
-    free (uzbl.state.uri);
+    g_free (uzbl.state.uri);
     GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
     uzbl.state.uri = g_string_free (newuri, FALSE);
     if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
         uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
         update_title();
     }
-    g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
     if (uzbl.behave.load_commit_handler)
         run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
 }
@@ -452,29 +580,38 @@ VIEWFUNC(go_forward)
 /* -- command to callback/function map for things we cannot attach to any signals */
 // TODO: reload
 static struct {char *name; Command command[2];} cmdlist[] =
-{   /* key                 function      no_split      */
-    { "back",             {view_go_back, 0}              },
-    { "forward",          {view_go_forward, 0}           },
-    { "scroll_vert",      {scroll_vert, 0}               },
-    { "scroll_horz",      {scroll_horz, 0}               },
-    { "scroll_begin",     {scroll_begin, 0}              },
-    { "scroll_end",       {scroll_end, 0}                },
-    { "reload",           {view_reload, 0},              },
-    { "reload_ign_cache", {view_reload_bypass_cache, 0}  },
-    { "stop",             {view_stop_loading, 0},        },
-    { "zoom_in",          {view_zoom_in, 0},             }, //Can crash (when max zoom reached?).
-    { "zoom_out",         {view_zoom_out, 0},            },
-    { "uri",              {load_uri, NOSPLIT}            },
-    { "js",               {run_js, NOSPLIT}              },
-    { "script",           {run_external_js, 0}           },
-    { "toggle_status",    {toggle_status_cb, 0}          },
-    { "spawn",            {spawn, 0}                     },
-    { "sh",               {spawn_sh, 0}                  },
-    { "exit",             {close_uzbl, 0}                },
-    { "search",           {search_forward_text, NOSPLIT} },
-    { "search_reverse",   {search_reverse_text, NOSPLIT} },
-    { "insert_mode",      {toggle_insert_mode, 0}        },
-    { "runcmd",           {runcmd, NOSPLIT}              }
+{   /* key                   function      no_split      */
+    { "back",               {view_go_back, 0}              },
+    { "forward",            {view_go_forward, 0}           },
+    { "scroll_vert",        {scroll_vert, 0}               },
+    { "scroll_horz",        {scroll_horz, 0}               },
+    { "scroll_begin",       {scroll_begin, 0}              },
+    { "scroll_end",         {scroll_end, 0}                },
+    { "reload",             {view_reload, 0},              },
+    { "reload_ign_cache",   {view_reload_bypass_cache, 0}  },
+    { "stop",               {view_stop_loading, 0},        },
+    { "zoom_in",            {view_zoom_in, 0},             }, //Can crash (when max zoom reached?).
+    { "zoom_out",           {view_zoom_out, 0},            },
+    { "uri",                {load_uri, NOSPLIT}            },
+    { "js",                 {run_js, NOSPLIT}              },
+    { "script",             {run_external_js, 0}           },
+    { "toggle_status",      {toggle_status_cb, 0}          },
+    { "spawn",              {spawn, 0}                     },
+    { "sync_spawn",         {spawn_sync, 0}                }, // needed for cookie handler
+    { "sh",                 {spawn_sh, 0}                  },
+    { "sync_sh",            {spawn_sh_sync, 0}             }, // needed for cookie handler
+    { "exit",               {close_uzbl, 0}                },
+    { "search",             {search_forward_text, NOSPLIT} },
+    { "search_reverse",     {search_reverse_text, NOSPLIT} },
+    { "dehilight",          {dehilight, 0}                 },
+    { "toggle_insert_mode", {toggle_insert_mode, 0}        },
+    { "runcmd",             {runcmd, NOSPLIT}              },
+    { "set",                {set_var, NOSPLIT}             },
+    { "dump_config",        {act_dump_config, 0}           },
+    { "keycmd",             {keycmd, NOSPLIT}              },
+    { "keycmd_nl",          {keycmd_nl, NOSPLIT}           },
+    { "keycmd_bs",          {keycmd_bs, 0}                 },
+    { "chain",              {chain, 0}                     }
 };
 
 static void
@@ -513,13 +650,27 @@ new_action(const gchar *name, const gchar *param) {
 
 static bool
 file_exists (const char * filename) {
-       return (access(filename, F_OK) == 0);
+    return (access(filename, F_OK) == 0);
+}
+
+static void
+set_var(WebKitWebView *page, GArray *argv) {
+    (void) page;
+    gchar *ctl_line;
+
+    ctl_line = g_strdup_printf("%s %s", "set", argv_idx(argv, 0));
+    parse_cmd_line(ctl_line);
+    g_free(ctl_line);
+}
+
+static void
+act_dump_config() {
+    dump_config();
 }
 
 static void
 toggle_insert_mode(WebKitWebView *page, GArray *argv) {
     (void)page;
-    (void)argv;
 
     if (argv_idx(argv, 0)) {
         if (strcmp (argv_idx(argv, 0), "0") == 0) {
@@ -540,7 +691,7 @@ load_uri (WebKitWebView *web_view, GArray *argv) {
         GString* newuri = g_string_new (argv_idx(argv, 0));
         if (g_strrstr (argv_idx(argv, 0), "://") == NULL)
             g_string_prepend (newuri, "http://");
-               /* if we do handle cookies, ask our handler for them */
+        /* if we do handle cookies, ask our handler for them */
         webkit_web_view_load_uri (web_view, newuri->str);
         g_string_free (newuri, TRUE);
     }
@@ -555,55 +706,49 @@ run_js (WebKitWebView * web_view, GArray *argv) {
 static void
 run_external_js (WebKitWebView * web_view, GArray *argv) {
     if (argv_idx(argv, 0)) {
-        gchar** lines = read_file_by_line (argv_idx (argv, 0));
+        GArray* lines = read_file_by_line (argv_idx (argv, 0));
         gchar*  js = NULL;
-        int i;
-
-        if (lines[0] != NULL) {
-            for (i = 0; lines[i] != NULL; i ++) {
-                if (js == NULL) {
-                    js = g_strdup (lines[i]);
-                } else {
-                    gchar* newjs = g_strconcat (js, lines[i], NULL);
-                    js = newjs;
-                }
-                //g_free (lines[i]); - Another mysterious breakage
-            }
-
-            if (uzbl.state.verbose)
-                printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
+        int i = 0;
+        gchar* line;
 
-            if (argv_idx (argv, 1)) {
-                gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
+        while ((line = g_array_index(lines, gchar*, i))) {
+            if (js == NULL) {
+                js = g_strdup (line);
+            } else {
+                gchar* newjs = g_strconcat (js, line, NULL);
                 js = newjs;
             }
-            webkit_web_view_execute_script (web_view, js);
+            i ++;
+            g_free (line);
+        }
+        
+        if (uzbl.state.verbose)
+            printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
+
+        if (argv_idx (argv, 1)) {
+            gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
             g_free (js);
-        } else {
-            fprintf(stderr, "JavaScript file '%s' not be read.\n", argv_idx(argv, 0));
+            js = newjs;
         }
+        webkit_web_view_execute_script (web_view, js);
+        g_free (js);
+        g_array_free (lines, TRUE);
     }
 }
 
 static void
 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
-    if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0'))
-        uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
-
-    if (uzbl.state.searchtx != NULL) {
-        if (uzbl.state.verbose)
-            printf ("Searching: %s\n", uzbl.state.searchtx);
-
-        if (g_strcmp0 (uzbl.state.searchtx, uzbl.state.searchold) != 0) {
+    if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
+        if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
             webkit_web_view_unmark_text_matches (page);
-            webkit_web_view_mark_text_matches (page, uzbl.state.searchtx, FALSE, 0);
-
-            if (uzbl.state.searchold != NULL)
-                g_free (uzbl.state.searchold);
-
-            uzbl.state.searchold = g_strdup (uzbl.state.searchtx);
+            webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
+            uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
         }
-
+    }
+    
+    if (uzbl.state.searchtx) {
+        if (uzbl.state.verbose)
+            printf ("Searching: %s\n", uzbl.state.searchtx);
         webkit_web_view_set_highlight_text_matches (page, TRUE);
         webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
     }
@@ -620,6 +765,13 @@ search_reverse_text (WebKitWebView *page, GArray *argv) {
 }
 
 static void
+dehilight (WebKitWebView *page, GArray *argv) {
+    (void) argv;
+    webkit_web_view_set_highlight_text_matches (page, FALSE);
+}
+
+
+static void
 new_window_load_uri (const gchar * uri) {
     GString* to_execute = g_string_new ("");
     g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
@@ -639,6 +791,45 @@ new_window_load_uri (const gchar * uri) {
 }
 
 static void
+chain (WebKitWebView *page, GArray *argv) {
+    (void)page;
+    gchar *a = NULL;
+    gchar **parts = NULL;
+    guint i = 0;    
+    while ((a = argv_idx(argv, i++))) {
+        parts = g_strsplit (a, " ", 2);
+        parse_command(parts[0], parts[1]);
+        g_strfreev (parts);
+    }
+}
+
+static void
+keycmd (WebKitWebView *page, GArray *argv) {
+    (void)page;
+    (void)argv;
+    g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
+    run_keycmd(FALSE);
+    update_title();
+}
+
+static void
+keycmd_nl (WebKitWebView *page, GArray *argv) {
+    (void)page;
+    (void)argv;
+    g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
+    run_keycmd(TRUE);
+    update_title();
+}
+
+static void
+keycmd_bs (WebKitWebView *page, GArray *argv) {
+    (void)page;
+    (void)argv;
+    g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
+    update_title();
+}
+
+static void
 close_uzbl (WebKitWebView *page, GArray *argv) {
     (void)page;
     (void)argv;
@@ -648,7 +839,7 @@ close_uzbl (WebKitWebView *page, GArray *argv) {
 /* --Statusbar functions-- */
 static char*
 build_progressbar_ascii(int percent) {
-   int width=10;
+   int width=uzbl.gui.sbar.progress_w;
    int i;
    double l;
    GString *bar = g_string_new("");
@@ -657,10 +848,10 @@ build_progressbar_ascii(int percent) {
    l = (int)(l+.5)>=(int)l ? l+.5 : l;
 
    for(i=0; i<(int)l; i++)
-       g_string_append(bar, "=");
+       g_string_append(bar, uzbl.gui.sbar.progress_s);
 
    for(; i<width; i++)
-       g_string_append(bar, "ยท");
+       g_string_append(bar, uzbl.gui.sbar.progress_u);
 
    return g_string_free(bar, FALSE);
 }
@@ -721,7 +912,7 @@ setup_scanner() {
 }
 
 static gchar *
-expand_template(const char *template) {
+expand_template(const char *template, gboolean escape_markup) {
      if(!template) return NULL;
 
      GTokenType token = G_TOKEN_NONE;
@@ -737,14 +928,20 @@ expand_template(const char *template) {
              sym = (int)g_scanner_cur_value(uzbl.scan).v_symbol;
              switch(sym) {
                  case SYM_URI:
-                     g_string_append(ret,
-                         uzbl.state.uri?
-                         g_markup_printf_escaped("%s", uzbl.state.uri):"");
+                     if(escape_markup) {
+                         buf = uzbl.state.uri?
+                             g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
+                         g_string_append(ret, buf);
+                         g_free(buf);
+                     }
+                     else
+                         g_string_append(ret, uzbl.state.uri?
+                                 uzbl.state.uri:g_strdup(""));
                      break;
                  case SYM_LOADPRGS:
                      buf = itos(uzbl.gui.sbar.load_progress);
                      g_string_append(ret, buf);
-                     free(buf);
+                     g_free(buf);
                      break;
                  case SYM_LOADPRGSBAR:
                      buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
@@ -752,49 +949,68 @@ expand_template(const char *template) {
                      g_free(buf);
                      break;
                  case SYM_TITLE:
-                     g_string_append(ret,
-                         uzbl.gui.main_title?
-                         g_markup_printf_escaped("%s", uzbl.gui.main_title):"");
+                     if(escape_markup) {
+                         buf = uzbl.gui.main_title?
+                             g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
+                         g_string_append(ret, buf);
+                         g_free(buf);
+                     }
+                     else
+                         g_string_append(ret, uzbl.gui.main_title?
+                                 uzbl.gui.main_title:g_strdup(""));
                      break;
                  case SYM_SELECTED_URI:
-                     g_string_append(ret,
-                         uzbl.state.selected_url?
-                         g_markup_printf_escaped("%s", uzbl.state.selected_url):"");
-                    break;
+                     if(escape_markup) {
+                         buf = uzbl.state.selected_url?
+                             g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
+                         g_string_append(ret, buf);
+                         g_free(buf);
+                     }
+                     else
+                         g_string_append(ret, uzbl.state.selected_url?
+                                 uzbl.state.selected_url:g_strdup(""));
+                     break;
                  case SYM_NAME:
                      buf = itos(uzbl.xwin);
                      g_string_append(ret,
-                         uzbl.state.instance_name?uzbl.state.instance_name:buf);
-                     free(buf);
+                             uzbl.state.instance_name?uzbl.state.instance_name:buf);
+                     g_free(buf);
                      break;
                  case SYM_KEYCMD:
-                     g_string_append(ret,
-                         uzbl.state.keycmd->str ?
-                         g_markup_printf_escaped("%s", uzbl.state.keycmd->str):"");
+                     if(escape_markup) {
+                         buf = uzbl.state.keycmd->str?
+                             g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
+                         g_string_append(ret, buf);
+                         g_free(buf);
+                     }
+                     else
+                         g_string_append(ret, uzbl.state.keycmd->str?
+                                 uzbl.state.keycmd->str:g_strdup(""));
                      break;
                  case SYM_MODE:
                      g_string_append(ret,
-                         uzbl.behave.insert_mode?"[I]":"[C]");
+                             uzbl.behave.insert_mode?
+                             uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
                      break;
                  case SYM_MSG:
                      g_string_append(ret,
-                         uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
+                             uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
                      break;
                      /* useragent syms */
                  case SYM_WK_MAJ:
                      buf = itos(WEBKIT_MAJOR_VERSION);
                      g_string_append(ret, buf);
-                     free(buf);
+                     g_free(buf);
                      break;
                  case SYM_WK_MIN:
                      buf = itos(WEBKIT_MINOR_VERSION);
                      g_string_append(ret, buf);
-                     free(buf);
+                     g_free(buf);
                      break;
                  case SYM_WK_MIC:
                      buf = itos(WEBKIT_MICRO_VERSION);
                      g_string_append(ret, buf);
-                     free(buf);
+                     g_free(buf);
                      break;
                  case SYM_SYSNAME:
                      g_string_append(ret, uzbl.state.unameinfo.sysname);
@@ -829,7 +1045,7 @@ expand_template(const char *template) {
          else if(token == G_TOKEN_INT) {
              buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
              g_string_append(ret, buf);
-             free(buf);
+             g_free(buf);
          }
          else if(token == G_TOKEN_IDENTIFIER) {
              g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
@@ -873,11 +1089,15 @@ run_command (const gchar *command, const guint npre, const gchar **args,
 
     for (i = npre; i < g_strv_length((gchar**)args); i++)
         sharg_append(a, args[i]);
+    
     gboolean result;
-    if (sync) result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
-                                    NULL, NULL, stdout, NULL, NULL, &err);
-    else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
-                                NULL, NULL, NULL, &err);
+    if (sync) {
+        if (*stdout) *stdout = strfree(*stdout);
+        
+        result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
+                              NULL, NULL, stdout, NULL, NULL, &err);
+    } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
+                                  NULL, NULL, NULL, &err);
 
     if (uzbl.state.verbose) {
         GString *s = g_string_new("spawned:");
@@ -933,7 +1153,7 @@ split_quoted(const gchar* src, const gboolean unquote) {
     g_array_append_val(a, dup);
     ret = (gchar**)a->data;
     g_array_free (a, FALSE);
-    g_string_free (s, FALSE);
+    g_string_free (s, TRUE);
     return ret;
 }
 
@@ -941,7 +1161,17 @@ static void
 spawn(WebKitWebView *web_view, GArray *argv) {
     (void)web_view;
     //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
-    if (argv_idx(argv, 0)) run_command(argv_idx(argv, 0), 0, (const gchar **) argv->data + sizeof(gchar*), FALSE, NULL);
+    if (argv_idx(argv, 0))
+        run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
+}
+
+static void
+spawn_sync(WebKitWebView *web_view, GArray *argv) {
+    (void)web_view;
+    
+    if (argv_idx(argv, 0))
+        run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
+                    TRUE, &uzbl.comm.sync_stdout);
 }
 
 static void
@@ -966,6 +1196,28 @@ spawn_sh(WebKitWebView *web_view, GArray *argv) {
 }
 
 static void
+spawn_sh_sync(WebKitWebView *web_view, GArray *argv) {
+    (void)web_view;
+    if (!uzbl.behave.shell_cmd) {
+        g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
+        return;
+    }
+    
+    guint i;
+    gchar *spacer = g_strdup("");
+    g_array_insert_val(argv, 1, spacer);
+    gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
+
+    for (i = 1; i < g_strv_length(cmd); i++)
+        g_array_prepend_val(argv, cmd[i]);
+         
+    if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
+                         TRUE, &uzbl.comm.sync_stdout);
+    g_free (spacer);
+    g_strfreev (cmd);
+}
+
+static void
 parse_command(const char *cmd, const char *param) {
     Command *c;
 
@@ -975,9 +1227,9 @@ parse_command(const char *cmd, const char *param) {
             gchar **par = split_quoted(param, TRUE);
             GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
 
-            if (c[1]) { /* don't split */
+            if (c[1] == NOSPLIT) { /* don't split */
                 sharg_append(a, param);
-            } else {
+            } else if (par) {
                 for (i = 0; i < g_strv_length(par); i++)
                     sharg_append(a, par[i]);
             }
@@ -1000,36 +1252,17 @@ setup_regex() {
             G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, NULL);
     uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
             G_REGEX_OPTIMIZE, 0, NULL);
-    uzbl.comm.keycmd_regex = g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
-            G_REGEX_OPTIMIZE, 0, NULL);
 }
 
 static gboolean
 get_var_value(gchar *name) {
-    void **p = NULL;
-
-    if( (p = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
-        if(var_is("uri", name)
-           || var_is("status_message", name)
-           || var_is("status_format", name)
-           || var_is("status_background", name)
-           || var_is("title_format_short", name)
-           || var_is("title_format_long", name)
-           || var_is("modkey", name)
-           || var_is("load_finish_handler", name)
-           || var_is("load_start_handler", name)
-           || var_is("load_commit_handler", name)
-           || var_is("history_handler", name)
-           || var_is("download_handler", name)
-           || var_is("cookie_handler", name)
-           || var_is("fifo_dir", name)
-           || var_is("socket_dir", name)
-           || var_is("shell_cmd", name)
-           || var_is("proxy_url", name)
-           || var_is("useragent", name))
-           {
-            printf("VAR: %s VALUE: %s\n", name, (char *)*p);
-        } else printf("VAR: %s VALUE: %d\n", name, (int)*p);
+    uzbl_cmdprop *c;
+
+    if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
+        if(c->type == TYPE_STR)
+            printf("VAR: %s VALUE: %s\n", name, (char *)*c->ptr);
+        else if(c->type == TYPE_INT)
+            printf("VAR: %s VALUE: %d\n", name, (int)*c->ptr);
     }
     return TRUE;
 }
@@ -1053,6 +1286,206 @@ set_proxy_url() {
     return;
 }
 
+static void
+cmd_load_uri() {
+    GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
+    g_array_append_val (a, uzbl.state.uri);
+    load_uri(uzbl.gui.web_view, a);
+    g_array_free (a, TRUE);
+}
+
+static void 
+cmd_always_insert_mode() {
+    uzbl.behave.insert_mode =
+        uzbl.behave.always_insert_mode ?  TRUE : FALSE;
+    update_title();
+}
+
+static void
+cmd_max_conns() {
+    g_object_set(G_OBJECT(uzbl.net.soup_session),
+            SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
+}
+
+static void
+cmd_max_conns_host() {
+    g_object_set(G_OBJECT(uzbl.net.soup_session),
+            SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
+}
+
+static void
+cmd_http_debug() {
+    soup_session_remove_feature
+        (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
+    /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
+    /*g_free(uzbl.net.soup_logger);*/
+
+    uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
+    soup_session_add_feature(uzbl.net.soup_session,
+            SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
+}
+
+static WebKitWebSettings*
+view_settings() {
+    return webkit_web_view_get_settings(uzbl.gui.web_view);
+}
+
+static void
+cmd_font_size() {
+    WebKitWebSettings *ws = view_settings();
+    if (uzbl.behave.font_size > 0) {
+        g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
+    }
+    
+    if (uzbl.behave.monospace_size > 0) {
+        g_object_set (G_OBJECT(ws), "default-monospace-font-size",
+                      uzbl.behave.monospace_size, NULL);
+    } else {
+        g_object_set (G_OBJECT(ws), "default-monospace-font-size",
+                      uzbl.behave.font_size, NULL);
+    }
+}
+
+static void
+cmd_disable_plugins() {
+    g_object_set (G_OBJECT(view_settings()), "enable-plugins", 
+            !uzbl.behave.disable_plugins, NULL);
+}
+
+static void
+cmd_disable_scripts() {
+    g_object_set (G_OBJECT(view_settings()), "enable-scripts",
+            !uzbl.behave.disable_scripts, NULL);
+}
+
+static void
+cmd_minimum_font_size() {
+    g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
+            uzbl.behave.minimum_font_size, NULL);
+}
+static void
+cmd_autoload_img() {
+    g_object_set (G_OBJECT(view_settings()), "auto-load-images",
+            uzbl.behave.autoload_img, NULL);
+}
+
+
+static void
+cmd_autoshrink_img() {
+    g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
+            uzbl.behave.autoshrink_img, NULL);
+}
+
+
+static void
+cmd_enable_spellcheck() {
+    g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
+            uzbl.behave.enable_spellcheck, NULL);
+}
+
+static void
+cmd_enable_private() {
+    g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
+            uzbl.behave.enable_private, NULL);
+}
+
+static void
+cmd_print_bg() {
+    g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
+            uzbl.behave.print_bg, NULL);
+}
+
+static void 
+cmd_style_uri() {
+    g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
+            uzbl.behave.style_uri, NULL);
+}
+
+static void 
+cmd_resizable_txt() {
+    g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
+            uzbl.behave.resizable_txt, NULL);
+}
+
+static void 
+cmd_default_encoding() {
+    g_object_set (G_OBJECT(view_settings()), "default-encoding",
+            uzbl.behave.default_encoding, NULL);
+}
+
+static void 
+cmd_enforce_96dpi() {
+    g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
+            uzbl.behave.enforce_96dpi, NULL);
+}
+
+static void 
+cmd_caret_browsing() {
+    g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
+            uzbl.behave.caret_browsing, NULL);
+}
+
+static void
+cmd_cookie_handler() {
+    gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
+    if ((g_strcmp0(split[0], "sh") == 0) ||
+        (g_strcmp0(split[0], "spawn") == 0)) {
+        g_free (uzbl.behave.cookie_handler);
+        uzbl.behave.cookie_handler =
+            g_strdup_printf("sync_%s %s", split[0], split[1]);
+    }
+    g_strfreev (split);
+}
+
+static void
+cmd_fifo_dir() {
+    uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
+}
+
+static void
+cmd_socket_dir() {
+    uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
+}
+
+static void
+cmd_inject_html() {
+    if(uzbl.behave.inject_html) {
+        webkit_web_view_load_html_string (uzbl.gui.web_view,
+                uzbl.behave.inject_html, NULL);
+    }
+}
+
+static void
+cmd_modkey() {
+    int i;
+    char *buf;
+
+    buf = g_utf8_strup(uzbl.behave.modkey, -1);
+    uzbl.behave.modmask = 0;
+
+    if(uzbl.behave.modkey) 
+        g_free(uzbl.behave.modkey);
+    uzbl.behave.modkey = buf;
+
+    for (i = 0; modkeys[i].key != NULL; i++) {
+        if (g_strrstr(buf, modkeys[i].key))
+            uzbl.behave.modmask |= modkeys[i].mask;
+    }
+}
+
+static void
+cmd_useragent() {
+    if (*uzbl.net.useragent == ' ') {
+        g_free (uzbl.net.useragent);
+        uzbl.net.useragent = NULL;
+    } else {
+        gchar *ua = expand_template(uzbl.net.useragent, FALSE);
+        if (ua)
+            g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
+        g_free(uzbl.net.useragent);
+        uzbl.net.useragent = ua;
+    }
+}
 
 static void
 move_statusbar() {
@@ -1072,119 +1505,30 @@ move_statusbar() {
     gtk_widget_unref(uzbl.gui.scrolled_win);
     gtk_widget_unref(uzbl.gui.mainbar);
     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
-}
-
-static gboolean
-var_is(const char *x, const char *y) {
-    return (strcmp(x, y) == 0 ? TRUE : FALSE );
+    return;
 }
 
 static gboolean
 set_var_value(gchar *name, gchar *val) {
-    void **p = NULL;
+    uzbl_cmdprop *c = NULL;
     char *endp = NULL;
-
-    if( (p = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
-        if(var_is("status_message", name)
-           || var_is("status_background", name)
-           || var_is("status_format", name)
-           || var_is("title_format_long", name)
-           || var_is("title_format_short", name)
-           || var_is("load_finish_handler", name)
-           || var_is("load_start_handler", name)
-           || var_is("load_commit_handler", name)
-           || var_is("history_handler", name)
-           || var_is("download_handler", name)
-           || var_is("cookie_handler", name)) {
-            if(*p)
-                free(*p);
-            *p = g_strdup(val);
-            update_title();
-        }
-        else if(var_is("uri", name)) {
-            if(*p) free(*p);
-            *p = g_strdup(val);
-            GArray *a = g_array_new(TRUE, FALSE, sizeof(gchar*));
-            g_array_append_val(a, *p);
-            load_uri(uzbl.gui.web_view, a);
-            g_array_free(a, TRUE);
-        }
-        else if(var_is("proxy_url", name)) {
-            if(*p) free(*p);
-            *p = g_strdup(val);
-            set_proxy_url();
-        }
-        else if(var_is("fifo_dir", name)) {
-            if(*p) free(*p);
-            *p = init_fifo(g_strdup(val));
-        }
-        else if(var_is("socket_dir", name)) {
-            if(*p) free(*p);
-            *p = init_socket(g_strdup(val));
-        }
-        else if(var_is("modkey", name)) {
-            if(*p) free(*p);
-            int i;
-            *p = g_utf8_strup(val, -1);
-            uzbl.behave.modmask = 0;
-            for (i = 0; modkeys[i].key != NULL; i++) {
-                if (g_strrstr(*p, modkeys[i].key))
-                    uzbl.behave.modmask |= modkeys[i].mask;
-            }
+    char *buf = NULL;
+
+    if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
+        /* check for the variable type */
+        if (c->type == TYPE_STR) {
+            buf = expand_vars(val);
+            g_free(*c->ptr);
+            *c->ptr = buf;
+        } else if(c->type == TYPE_INT) {
+            int *ip = (int *)c->ptr;
+            buf = expand_vars(val);
+            *ip = (int)strtoul(buf, &endp, 10);
+            g_free(buf);
         }
-        else if(var_is("useragent", name)) {
-            if(*p) free(*p);
-            *p = set_useragent(g_strdup(val));
-        }
-        else if(var_is("shell_cmd", name)) {
-            if(*p) free(*p);
-            *p = g_strdup(val);
-        }
-        /* variables that take int values */
-        else {
-            int *ip = (int *)p;
-            *ip = (int)strtoul(val, &endp, 10);
 
-            if(var_is("show_status", name)) {
-                cmd_set_status();
-            }
-            else if(var_is("always_insert_mode", name)) {
-                uzbl.behave.insert_mode =
-                    uzbl.behave.always_insert_mode ?  TRUE : FALSE;
-                update_title();
-            }
-            else if (var_is("max_conns", name)) {
-                g_object_set(G_OBJECT(uzbl.net.soup_session),
-                             SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
-            }
-            else if (var_is("max_conns_host", name)) {
-                g_object_set(G_OBJECT(uzbl.net.soup_session),
-                             SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
-            }
-            else if (var_is("http_debug", name)) {
-                //soup_session_remove_feature
-                //    (uzbl.net.soup_session, uzbl.net.soup_logger);
-                soup_session_remove_feature
-                    (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
-                /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
-                /*g_free(uzbl.net.soup_logger);*/
-
-                uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
-                soup_session_add_feature(uzbl.net.soup_session,
-                                         SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
-            }
-            else if (var_is("status_top", name)) {
-                move_statusbar();
-            }
-            else if (var_is("default_font_size", name)) {
-                WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
-                g_object_set (G_OBJECT(ws), "default-font-size", *ip, NULL);
-            }
-            else if (var_is("minimum_font_size", name)) {
-                WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
-                g_object_set (G_OBJECT(ws), "minimum-font-size", *ip, NULL);
-            }
-        }
+        /* invoke a command specific function */
+        if(c->func) c->func();
     }
     return TRUE;
 }
@@ -1196,72 +1540,91 @@ runcmd(WebKitWebView* page, GArray *argv) {
 }
 
 static void
+render_html() {
+    Behaviour *b = &uzbl.behave;
+
+    if(b->html_buffer->str) {
+        webkit_web_view_load_html_string (uzbl.gui.web_view,
+                b->html_buffer->str, b->base_url);
+        g_string_free(b->html_buffer, TRUE);
+        b->html_buffer = g_string_new("");
+    }
+}
+
+enum {M_CMD, M_HTML};
+static void
 parse_cmd_line(const char *ctl_line) {
-    gchar **tokens;
-
-    /* SET command */
-    if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
-        tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
-        if(tokens[0][0] == 0) {
-            gchar* value = parseenv (tokens[2]);
-            set_var_value(tokens[1], value);
-            g_strfreev(tokens);
-            g_free(value);
+    gchar **tokens = NULL;
+    Behaviour *b = &uzbl.behave;
+    size_t len=0;
+
+    if(b->mode == M_HTML) {
+        len = strlen(b->html_endmarker);
+        /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
+        if(len == strlen(ctl_line)-1 &&
+           !strncmp(b->html_endmarker, ctl_line, len)) {
+            set_timeout(0);
+            set_var_value("mode", "0");
+            render_html();
+            return;
         }
-        else
-            printf("Error in command: %s\n", tokens[0]);
-    }
-    /* GET command */
-    else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
-        tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
-        if(tokens[0][0] == 0) {
-            get_var_value(tokens[1]);
-            g_strfreev(tokens);
+        else {
+            set_timeout(b->html_timeout);
+            g_string_append(b->html_buffer, ctl_line);
         }
-        else
-            printf("Error in command: %s\n", tokens[0]);
     }
-    /* BIND command */
-    else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
-        tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
-        if(tokens[0][0] == 0) {
-            gchar* value = parseenv (tokens[2]);
-            add_binding(tokens[1], value);
-            g_strfreev(tokens);
-            g_free(value);
+    else {
+        /* SET command */
+        if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
+            tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
+            if(tokens[0][0] == 0) {
+                gchar* value = parseenv(g_strdup(tokens[2]));
+                set_var_value(tokens[1], value);
+                g_free(value);
+            }
+            else
+                printf("Error in command: %s\n", tokens[0]);
         }
-        else
-            printf("Error in command: %s\n", tokens[0]);
-    }
-    /* ACT command */
-    else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
-        tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
-        if(tokens[0][0] == 0) {
-            parse_command(tokens[1], tokens[2]);
-            g_strfreev(tokens);
+        /* GET command */
+        else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
+            tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
+            if(tokens[0][0] == 0) {
+                get_var_value(tokens[1]);
+            }
+            else
+                printf("Error in command: %s\n", tokens[0]);
         }
+        /* BIND command */
+        else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
+            tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
+            if(tokens[0][0] == 0) {
+                gchar* value = parseenv(g_strdup(tokens[2]));
+                add_binding(tokens[1], value);
+                g_free(value);
+            }
+            else
+                printf("Error in command: %s\n", tokens[0]);
+        }
+        /* ACT command */
+        else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
+            tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
+            if(tokens[0][0] == 0) {
+                parse_command(tokens[1], tokens[2]);
+            }
+            else
+                printf("Error in command: %s\n", tokens[0]);
+        }
+        /* Comments */
+        else if(   (ctl_line[0] == '#')
+                || (ctl_line[0] == ' ')
+                || (ctl_line[0] == '\n'))
+            ; /* ignore these lines */
         else
-            printf("Error in command: %s\n", tokens[0]);
-    }
-    /* KEYCMD command */
-    else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
-        tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
-        if(tokens[0][0] == 0) {
-            /* should incremental commands want each individual "keystroke"
-               sent in a loop or the whole string in one go like now? */
-            g_string_assign(uzbl.state.keycmd, tokens[1]);
-            run_keycmd(FALSE);
-            update_title();
+            printf("Command not understood (%s)\n", ctl_line);
+
+        if(tokens)
             g_strfreev(tokens);
-        }
     }
-    /* Comments */
-    else if(   (ctl_line[0] == '#')
-            || (ctl_line[0] == ' ')
-            || (ctl_line[0] == '\n'))
-        ; /* ignore these lines */
-    else
-        printf("Command not understood (%s)\n", ctl_line);
 
     return;
 }
@@ -1322,7 +1685,7 @@ init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
     }
 
     if (*dir == ' ') { /* space unsets the variable */
-        g_free(dir);
+        g_free (dir);
         return NULL;
     }
 
@@ -1347,23 +1710,18 @@ init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
 
     /* if we got this far, there was an error; cleanup */
     if (error) g_error_free (error);
-    g_free(path);
     g_free(dir);
+    g_free(path);
     return NULL;
 }
 
 static gboolean
 control_stdin(GIOChannel *gio, GIOCondition condition) {
+    (void) condition;
     gchar *ctl_line = NULL;
-    gsize ctl_line_len = 0;
     GIOStatus ret;
 
-    if (condition & G_IO_HUP) {
-        ret = g_io_channel_shutdown (gio, FALSE, NULL);
-        return FALSE;
-    }
-
-    ret = g_io_channel_read_line(gio, &ctl_line, &ctl_line_len, NULL, NULL);
+    ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
     if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
         return FALSE;
 
@@ -1502,12 +1860,12 @@ update_title (void) {
 
     if (b->show_status) {
         if (b->title_format_short) {
-            parsed = expand_template(b->title_format_short);
+            parsed = expand_template(b->title_format_short, FALSE);
             gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
             g_free(parsed);
         }
         if (b->status_format) {
-            parsed = expand_template(b->status_format);
+            parsed = expand_template(b->status_format, TRUE);
             gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
             g_free(parsed);
         }
@@ -1519,7 +1877,7 @@ update_title (void) {
         }
     } else {
         if (b->title_format_long) {
-            parsed = expand_template(b->title_format_long);
+            parsed = expand_template(b->title_format_long, FALSE);
             gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
             g_free(parsed);
         }
@@ -1527,11 +1885,11 @@ update_title (void) {
 }
 
 static gboolean
-key_press_cb (WebKitWebView* page, GdkEventKey* event)
+key_press_cb (GtkWidget* window, GdkEventKey* event)
 {
     //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
 
-    (void) page;
+    (void) window;
 
     if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
         || event->keyval == GDK_Up || event->keyval == GDK_Down || event->keyval == GDK_Left || event->keyval == GDK_Right || event->keyval == GDK_Shift_L || event->keyval == GDK_Shift_R)
@@ -1550,6 +1908,7 @@ key_press_cb (WebKitWebView* page, GdkEventKey* event)
     if (event->keyval == GDK_Escape) {
         g_string_truncate(uzbl.state.keycmd, 0);
         update_title();
+        dehilight(uzbl.gui.web_view, NULL);
         return TRUE;
     }
 
@@ -1564,15 +1923,13 @@ key_press_cb (WebKitWebView* page, GdkEventKey* event)
         if (str) {
             g_string_append (uzbl.state.keycmd, str);
             update_title ();
-            free (str);
+            g_free (str);
         }
         return TRUE;
     }
 
-    if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
-        g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
-        update_title();
-    }
+    if (event->keyval == GDK_BackSpace)
+        keycmd_bs(NULL, NULL);
 
     gboolean key_ret = FALSE;
     if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
@@ -1588,57 +1945,58 @@ key_press_cb (WebKitWebView* page, GdkEventKey* event)
 static void
 run_keycmd(const gboolean key_ret) {
     /* run the keycmd immediately if it isn't incremental and doesn't take args */
-    Action *action;
-    if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
+    Action *act;
+    if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
         g_string_truncate(uzbl.state.keycmd, 0);
-        parse_command(action->name, action->param);
+        parse_command(act->name, act->param);
         return;
     }
 
     /* try if it's an incremental keycmd or one that takes args, and run it */
     GString* short_keys = g_string_new ("");
     GString* short_keys_inc = g_string_new ("");
-    unsigned int i;
+    guint i;
     for (i=0; i<(uzbl.state.keycmd->len); i++) {
         g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
         g_string_assign(short_keys_inc, short_keys->str);
         g_string_append_c(short_keys, '_');
         g_string_append_c(short_keys_inc, '*');
 
-        gboolean exec_now = FALSE;
-        if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
-            if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
-        } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
-            if (key_ret) { /* just quit the incremental command on return */
-                g_string_truncate(uzbl.state.keycmd, 0);
-                break;
-            } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
-        }
-
-        if (exec_now) {
-            GString* parampart = g_string_new (uzbl.state.keycmd->str);
-            GString* actionname = g_string_new ("");
-            GString* actionparam = g_string_new ("");
-            g_string_erase (parampart, 0, i+1);
-            if (action->name)
-                g_string_printf (actionname, action->name, parampart->str);
-            if (action->param)
-                g_string_printf (actionparam, action->param, parampart->str);
-            parse_command(actionname->str, actionparam->str);
-            g_string_free (actionname, TRUE);
-            g_string_free (actionparam, TRUE);
-            g_string_free (parampart, TRUE);
-            if (key_ret)
+        if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
+            /* run normal cmds only if return was pressed */
+            exec_paramcmd(act, i);
+            g_string_truncate(uzbl.state.keycmd, 0);
+            break;
+        } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
+            if (key_ret)  /* just quit the incremental command on return */
                 g_string_truncate(uzbl.state.keycmd, 0);
+            else exec_paramcmd(act, i); /* otherwise execute the incremental */
             break;
         }
-
+        
         g_string_truncate(short_keys, short_keys->len - 1);
     }
     g_string_free (short_keys, TRUE);
     g_string_free (short_keys_inc, TRUE);
 }
 
+static void
+exec_paramcmd(const Action *act, const guint i) {
+    GString *parampart = g_string_new (uzbl.state.keycmd->str);
+    GString *actionname = g_string_new ("");
+    GString *actionparam = g_string_new ("");
+    g_string_erase (parampart, 0, i+1);
+    if (act->name)
+        g_string_printf (actionname, act->name, parampart->str);
+    if (act->param)
+        g_string_printf (actionparam, act->param, parampart->str);
+    parse_command(actionname->str, actionparam->str);
+    g_string_free(actionname, TRUE);
+    g_string_free(actionparam, TRUE);
+    g_string_free(parampart, TRUE);
+}
+
+
 static GtkWidget*
 create_browser () {
     GUI *g = &uzbl.gui;
@@ -1657,7 +2015,6 @@ create_browser () {
     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
     g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
-    g_signal_connect (G_OBJECT (g->web_view), "key-press-event", G_CALLBACK (key_press_cb), g->web_view);
     g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
     g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
     g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
@@ -1689,6 +2046,7 @@ GtkWidget* create_window () {
     gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
     gtk_widget_set_name (window, "Uzbl browser");
     g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
+    g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
 
     return window;
 }
@@ -1698,13 +2056,16 @@ run_handler (const gchar *act, const gchar *args) {
     char **parts = g_strsplit(act, " ", 2);
     if (!parts) return;
     else if ((g_strcmp0(parts[0], "spawn") == 0)
-             || (g_strcmp0(parts[0], "sh") == 0)) {
+             || (g_strcmp0(parts[0], "sh") == 0)
+             || (g_strcmp0(parts[0], "sync_spawn") == 0)
+             || (g_strcmp0(parts[0], "sync_sh") == 0)) {
         guint i;
         GString *a = g_string_new ("");
         char **spawnparts;
         spawnparts = split_quoted(parts[1], FALSE);
         g_string_append_printf(a, "%s", spawnparts[0]);
         if (args) g_string_append_printf(a, " %s", args); /* append handler args before user args */
+        
         for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
             g_string_append_printf(a, " %s", spawnparts[i]);
         parse_command(parts[0], a->str);
@@ -1728,10 +2089,7 @@ add_binding (const gchar *key, const gchar *act) {
         printf ("Binding %-10s : %s\n", key, act);
     action = new_action(parts[0], parts[1]);
 
-    if(g_hash_table_lookup(uzbl.bindings, key))
-        g_hash_table_remove(uzbl.bindings, key);
-    g_hash_table_insert(uzbl.bindings, g_strdup(key), action);
-
+    g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
     g_strfreev(parts);
 }
 
@@ -1740,16 +2098,15 @@ get_xdg_var (XDG_Var xdg) {
     const gchar* actual_value = getenv (xdg.environmental);
     const gchar* home         = getenv ("HOME");
 
-    gchar* return_value = str_replace ("~", home, g_strdup (actual_value));
+    gchar* return_value = str_replace ("~", home, actual_value);
 
     if (! actual_value || strcmp (actual_value, "") == 0) {
         if (xdg.default_value) {
-            return_value = str_replace ("~", home, g_strdup (xdg.default_value));
+            return_value = str_replace ("~", home, xdg.default_value);
         } else {
             return_value = NULL;
         }
     }
-
     return return_value;
 }
 
@@ -1759,23 +2116,26 @@ find_xdg_file (int xdg_type, char* filename) {
        xdg_type = 1 => data
        xdg_type = 2 => cache*/
 
-    gchar* temporary_file   = (char *)malloc (1024);
-    gchar* temporary_string = NULL;
-    char*  saveptr;
-
-    strcpy (temporary_file, get_xdg_var (XDG[xdg_type]));
+    gchar* xdgv = get_xdg_var (XDG[xdg_type]);
+    gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
+    g_free (xdgv);
 
-    strcat (temporary_file, filename);
+    gchar* temporary_string;
+    char*  saveptr;
+    char*  buf;
 
     if (! file_exists (temporary_file) && xdg_type != 2) {
-        temporary_string = (char *) strtok_r (get_xdg_var (XDG[3 + xdg_type]), ":", &saveptr);
-        
-        while (temporary_string && ! file_exists (temporary_file)) {
-            strcpy (temporary_file, temporary_string);
-            strcat (temporary_file, filename);
-            temporary_string = (char * ) strtok_r (NULL, ":", &saveptr);
+        buf = get_xdg_var (XDG[3 + xdg_type]);
+        temporary_string = (char *) strtok_r (buf, ":", &saveptr);
+        g_free(buf);
+
+        while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
+            g_free (temporary_file);
+            temporary_file = g_strconcat (temporary_string, filename, NULL);
         }
     }
+    
+    //g_free (temporary_string); - segfaults.
 
     if (file_exists (temporary_file)) {
         return temporary_file;
@@ -1783,85 +2143,55 @@ find_xdg_file (int xdg_type, char* filename) {
         return NULL;
     }
 }
-
 static void
 settings_init () {
     State *s = &uzbl.state;
     Network *n = &uzbl.net;
-
-    uzbl.behave.reset_command_mode = 1;
+    int i;
+    for (i = 0; default_config[i].command != NULL; i++) {
+        parse_cmd_line(default_config[i].command);
+    }
 
     if (!s->config_file) {
-        s->config_file = g_strdup (find_xdg_file (0, "/uzbl/config"));
+        s->config_file = find_xdg_file (0, "/uzbl/config");
     }
 
     if (s->config_file) {
-        GIOChannel *chan = NULL;
-        gchar *readbuf = NULL;
-        gsize len;
-
-        chan = g_io_channel_new_file(s->config_file, "r", NULL);
-
-        if (chan) {
-            while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL)
-                    == G_IO_STATUS_NORMAL) {
-                parse_cmd_line(readbuf);
-                g_free (readbuf);
-            }
+        GArray* lines = read_file_by_line (s->config_file);
+        int i = 0;
+        gchar* line;
 
-            g_io_channel_unref (chan);
-            if (uzbl.state.verbose)
-                printf ("Config %s loaded\n", s->config_file);
-        } else {
-            fprintf(stderr, "uzbl: error loading file%s\n", s->config_file);
+        while ((line = g_array_index(lines, gchar*, i))) {
+            parse_cmd_line (line);
+            i ++;
+            g_free (line);
         }
+        g_array_free (lines, TRUE);
     } else {
         if (uzbl.state.verbose)
             printf ("No configuration file loaded.\n");
     }
-    if (!uzbl.behave.status_format)
-        set_var_value("status_format", STATUS_DEFAULT);
-    if (!uzbl.behave.title_format_long)
-        set_var_value("title_format_long", TITLE_LONG_DEFAULT);
-    if (!uzbl.behave.title_format_short)
-        set_var_value("title_format_short", TITLE_SHORT_DEFAULT);
-
 
     g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
 }
 
-static gchar*
-set_useragent(gchar *val) {
-    if (*val == ' ') {
-        g_free(val);
-        return NULL;
-    }
-    gchar *ua = expand_template(val);
-    if (ua)
-        g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
-    return ua;
-}
-
 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
     (void) session;
     (void) user_data;
     if (!uzbl.behave.cookie_handler) return;
 
-    gchar * stdout = NULL;
     soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
-    GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
-    gchar *action = g_strdup ("GET");
+    GString *s = g_string_new ("");
     SoupURI * soup_uri = soup_message_get_uri(msg);
-    sharg_append(a, action);
-    sharg_append(a, soup_uri->host);
-    sharg_append(a, soup_uri->path);
-    run_command(uzbl.behave.cookie_handler, 0, (const gchar **) a->data, TRUE, &stdout); /* TODO: use handler */
-    //run_handler(uzbl.behave.cookie_handler); /* TODO: global stdout pointer, spawn_sync */
-    if(stdout) {
-        soup_message_headers_replace (msg->request_headers, "Cookie", stdout);
-    }
-    g_free (action);
-    g_array_free(a, TRUE);
+    g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
+    run_handler(uzbl.behave.cookie_handler, s->str);
+
+    if(uzbl.comm.sync_stdout)
+        soup_message_headers_replace (msg->request_headers, "Cookie", uzbl.comm.sync_stdout);
+    //printf("stdout: %s\n", uzbl.comm.sync_stdout);   // debugging
+    if (uzbl.comm.sync_stdout) uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
+        
+    g_string_free(s, TRUE);
 }
 
 static void
@@ -1871,33 +2201,152 @@ save_cookies (SoupMessage *msg, gpointer user_data){
     char *cookie;
     for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
         cookie = soup_cookie_to_set_cookie_header(ck->data);
-        GArray *a = g_array_new(TRUE, FALSE, sizeof(gchar*));
         SoupURI * soup_uri = soup_message_get_uri(msg);
-        gchar *action = strdup("PUT");
-        sharg_append(a, action);
-        sharg_append(a, soup_uri->host);
-        sharg_append(a, soup_uri->path);
-        sharg_append(a, cookie);
-        run_command(uzbl.behave.cookie_handler, 0, (const gchar **) a->data, FALSE, NULL);
+        GString *s = g_string_new ("");
+        g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
+        run_handler(uzbl.behave.cookie_handler, s->str);
         g_free (cookie);
-        g_free (action);
-        g_array_free(a, TRUE);
+        g_string_free(s, TRUE);
     }
     g_slist_free(ck);
 }
 
+/* --- WEBINSPECTOR --- */
+static void
+hide_window_cb(GtkWidget *widget, gpointer data) {
+    (void) data;
+
+    gtk_widget_hide(widget);
+}
+
+static WebKitWebView*
+create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
+    (void) data;
+    (void) page;
+    (void) web_inspector;
+    GtkWidget* scrolled_window;
+    GtkWidget* new_web_view;
+    GUI *g = &uzbl.gui;
+
+    g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
+            G_CALLBACK(hide_window_cb), NULL);
+
+    gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
+    gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
+    gtk_widget_show(g->inspector_window);
+
+    scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+            GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+    gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
+    gtk_widget_show(scrolled_window);
+
+    new_web_view = webkit_web_view_new();
+    gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
+
+    return WEBKIT_WEB_VIEW(new_web_view);
+}
+
+static gboolean
+inspector_show_window_cb (WebKitWebInspector* inspector){
+    (void) inspector;
+    gtk_widget_show(uzbl.gui.inspector_window);
+    return TRUE;
+}
+
+/* TODO: Add variables and code to make use of these functions */
+static gboolean
+inspector_close_window_cb (WebKitWebInspector* inspector){
+    (void) inspector;
+    return TRUE;
+}
+
+static gboolean
+inspector_attach_window_cb (WebKitWebInspector* inspector){
+    (void) inspector;
+    return FALSE;
+}
+
+static gboolean
+inspector_dettach_window_cb (WebKitWebInspector* inspector){
+    (void) inspector;
+    return FALSE;
+}
+
+static gboolean
+inspector_uri_changed_cb (WebKitWebInspector* inspector){
+    (void) inspector;
+    return FALSE;
+}
+
+static gboolean
+inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
+    (void) inspector;
+    return FALSE;
+}
+
+static void
+set_up_inspector() {
+    GUI *g = &uzbl.gui;
+    WebKitWebSettings *settings = view_settings();
+    g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
+
+    uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
+    g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
+    g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
+    g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
+    g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
+    g_signal_connect (G_OBJECT (g->inspector), "dettach-window", G_CALLBACK (inspector_dettach_window_cb), NULL);
+    g_signal_connect (G_OBJECT (g->inspector), "destroy", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
+
+    g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
+}
+
+static void
+dump_var_hash(gpointer k, gpointer v, gpointer ud) {
+    (void) ud;
+    uzbl_cmdprop *c = v;
+
+    if(!c->dump)
+        return;
+
+    if(c->type == TYPE_STR)
+        printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
+    else if(c->type == TYPE_INT)
+        printf("set %s = %d\n", (char *)k, (int)*c->ptr);
+}
+
+static void
+dump_key_hash(gpointer k, gpointer v, gpointer ud) {
+    (void) ud;
+    Action *a = v;
+
+    printf("bind %s = %s %s\n", (char *)k ,
+            (char *)a->name, a->param?(char *)a->param:"");
+}
+
+static void
+dump_config() {
+    g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
+    g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
+}
+
+/** -- MAIN -- **/
 int
 main (int argc, char* argv[]) {
     gtk_init (&argc, &argv);
     if (!g_thread_supported ())
         g_thread_init (NULL);
-
-    strcpy(uzbl.state.executable_path,argv[0]);
+    uzbl.state.executable_path = g_strdup(argv[0]);
+    uzbl.state.selected_url = NULL;
+    uzbl.state.searchtx = NULL;
 
     GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
     g_option_context_add_main_entries (context, entries, NULL);
     g_option_context_add_group (context, gtk_get_option_group (TRUE));
     g_option_context_parse (context, &argc, &argv, NULL);
+    g_option_context_free(context);
     /* initialize hash table */
     uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
 
@@ -1908,10 +2357,27 @@ main (int argc, char* argv[]) {
         fprintf(stderr, "uzbl: error hooking SIGTERM\n");
     if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
         fprintf(stderr, "uzbl: error hooking SIGINT\n");
+    if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
+        fprintf(stderr, "uzbl: error hooking SIGALARM\n");
+
 
     if(uname(&uzbl.state.unameinfo) == -1)
         g_printerr("Can't retrieve unameinfo.  Your useragent might appear wrong.\n");
 
+    uzbl.gui.sbar.progress_s = g_strdup("=");
+    uzbl.gui.sbar.progress_u = g_strdup("ยท");
+    uzbl.gui.sbar.progress_w = 10;
+
+    /* HTML mode defaults*/
+    uzbl.behave.html_buffer = g_string_new("");
+    uzbl.behave.html_endmarker = g_strdup(".");
+    uzbl.behave.html_timeout = 60;
+    uzbl.behave.base_url = g_strdup("http://invalid");
+
+    /* default mode indicators */
+    uzbl.behave.insert_indicator = g_strdup("I");
+    uzbl.behave.cmd_indicator    = g_strdup("C");
+
     setup_regex();
     setup_scanner();
     commands_hash ();
@@ -1929,7 +2395,6 @@ main (int argc, char* argv[]) {
     uzbl.gui.main_window = create_window ();
     gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
 
-    //load_uri (uzbl.gui.web_view, uzbl.state.uri); //TODO: is this needed?
 
     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
     gtk_widget_show_all (uzbl.gui.main_window);
@@ -1955,11 +2420,17 @@ main (int argc, char* argv[]) {
     else
         update_title();
 
-    create_stdin();
+    /* WebInspector */
+    set_up_inspector();
 
-    //if(uzbl.state.uri)
-    //    load_uri (uzbl.gui.web_view, uzbl.state.uri);
+    create_stdin();
 
+    if(uzbl.state.uri) {
+        GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
+        g_array_append_val(a, uzbl.state.uri);
+        load_uri (uzbl.gui.web_view, a);
+        g_array_free (a, TRUE);
+    }
 
     gtk_main ();
     clean_up();