Merge commit 'Dieterbe/experimental' into experimental
[uzbl-mobile] / uzbl.c
diff --git a/uzbl.c b/uzbl.c
index 28ff64f..a18c7d5 100644 (file)
--- a/uzbl.c
+++ b/uzbl.c
@@ -32,6 +32,7 @@
 
 #define LENGTH(x) (sizeof x / sizeof x[0])
 #define MAX_BINDINGS 256
+#define _POSIX_SOURCE
 
 #include <gtk/gtk.h>
 #include <gdk/gdkx.h>
 #include <sys/types.h>
 #include <sys/un.h>
 #include <sys/utsname.h>
+#include <sys/time.h>
 #include <webkit/webkit.h>
+#include <libsoup/soup.h>
+
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <errno.h>
-#include <string.h>
 #include <fcntl.h>
-#include <sys/socket.h>
-#include <sys/un.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 GOptionEntry entries[] =
+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" },
+    { "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" },
+    { "socket",  's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,   
+        "Socket ID", "SOCKET" },
     { 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};
+enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
 
 /* an abbreviation to help keep the table's width humane */
-#define PTR(var, t, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .func = fun }
+#define PTR(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .func = fun }
 
 const struct {
     char *name;
     uzbl_cmdprop cp;
 } var_name_to_ptr[] = {
-/*    variable name         pointer to variable in code          type  callback function    */
+/*    variable name         pointer to variable in code          type  dump callback function    */
 /*  --------------------------------------------------------------------------------------- */
-    { "uri",                 PTR(uzbl.state.uri,                  STR, cmd_load_uri)},
-    { "status_message",      PTR(uzbl.gui.sbar.msg,               STR, update_title)},
-    { "show_status",         PTR(uzbl.behave.show_status,         INT, cmd_set_status)},
-    { "status_top",          PTR(uzbl.behave.status_top,          INT, move_statusbar)},
-    { "status_format",       PTR(uzbl.behave.status_format,       STR, update_title)},
-    { "status_pbar_done",    PTR(uzbl.gui.sbar.progress_s,        STR, update_title)},
-    { "status_pbar_pending", PTR(uzbl.gui.sbar.progress_u,        STR, update_title)},
-    { "status_pbar_width",   PTR(uzbl.gui.sbar.progress_w,        INT, update_title)},
-    { "status_background",   PTR(uzbl.behave.status_background,   STR, update_title)},
-    { "title_format_long",   PTR(uzbl.behave.title_format_long,   STR, update_title)},
-    { "title_format_short",  PTR(uzbl.behave.title_format_short,  STR, update_title)},
-    { "insert_mode",         PTR(uzbl.behave.insert_mode,         INT, NULL)},
-    { "always_insert_mode",  PTR(uzbl.behave.always_insert_mode,  INT, cmd_always_insert_mode)},
-    { "reset_command_mode",  PTR(uzbl.behave.reset_command_mode,  INT, NULL)},
-    { "modkey",              PTR(uzbl.behave.modkey,              STR, cmd_modkey)},
-    { "load_finish_handler", PTR(uzbl.behave.load_finish_handler, STR, NULL)},
-    { "load_start_handler",  PTR(uzbl.behave.load_start_handler,  STR, NULL)},
-    { "load_commit_handler", PTR(uzbl.behave.load_commit_handler, STR, NULL)},
-    { "history_handler",     PTR(uzbl.behave.history_handler,     STR, NULL)},
-    { "download_handler",    PTR(uzbl.behave.download_handler,    STR, NULL)},
-    { "cookie_handler",      PTR(uzbl.behave.cookie_handler,      STR, NULL)},
-    { "fifo_dir",            PTR(uzbl.behave.fifo_dir,            STR, cmd_fifo_dir)},
-    { "socket_dir",          PTR(uzbl.behave.socket_dir,          STR, cmd_socket_dir)},
-    { "http_debug",          PTR(uzbl.behave.http_debug,          INT, cmd_http_debug)},
-    { "font_size",           PTR(uzbl.behave.font_size,           INT, cmd_font_size)},
-    { "monospace_size",      PTR(uzbl.behave.monospace_size,      INT, cmd_font_size)},
-    { "minimum_font_size",   PTR(uzbl.behave.minimum_font_size,   INT, cmd_minimum_font_size)},
-    { "disable_plugins",     PTR(uzbl.behave.disable_plugins,     INT, cmd_disable_plugins)},
-    { "shell_cmd",           PTR(uzbl.behave.shell_cmd,           STR, NULL)},
-    { "proxy_url",           PTR(uzbl.net.proxy_url,              STR, set_proxy_url)},
-    { "max_conns",           PTR(uzbl.net.max_conns,              INT, cmd_max_conns)},
-    { "max_conns_host",      PTR(uzbl.net.max_conns_host,         INT, cmd_max_conns_host)},
-    { "useragent",           PTR(uzbl.net.useragent,              STR, cmd_useragent)},
-    { NULL,                  {.ptr = NULL, .type = TYPE_INT, .func = NULL}}
+    { "uri",                 PTR(uzbl.state.uri,                  STR,  1,   cmd_load_uri)},
+    { "verbose",             PTR(uzbl.state.verbose,              INT,  1,   NULL)},
+    { "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)},
+    { "icon",                PTR(uzbl.gui.icon,                   STR,  1,   set_icon)},
+    { "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 */
+    { "zoom_level",          PTR(uzbl.behave.zoom_level,          FLOAT,1,   cmd_zoom_level)},
+    { "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 {
@@ -158,8 +190,51 @@ make_var_to_name_hash() {
     }
 }
 
-
 /* --- UTILITY FUNCTIONS --- */
+static gchar *
+expand_vars(char *s) {
+    uzbl_cmdprop *c;
+    char upto = ' ';
+    char ret[256],  *vend;
+    GString *buf = g_string_new("");
+
+    while(*s) {
+        switch(*s) {
+            case '\\':
+                g_string_append_c(buf, *++s);
+                s++;
+                break;
+            case '@':
+                if(*(s+1) == '{') {
+                    upto = '}'; s++;
+                }
+                s++;
+                if( (vend = strchr(s, upto)) ||
+                        (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);
+                        }
+                    }
+                    if(upto == ' ') s = vend;
+                    else s = vend+1;
+                    upto = ' ';
+                }
+                break;
+            default:
+                g_string_append_c(buf, *s);
+                s++;
+                break;
+        }
+    }
+    return g_string_free(buf, FALSE);
+}
 
 char *
 itos(int val) {
@@ -170,6 +245,9 @@ 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 *
@@ -263,6 +341,19 @@ clean_up(void) {
     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 --- */
 
@@ -279,6 +370,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
@@ -295,6 +395,23 @@ new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequ
     return (FALSE);
 }
 
+static gboolean
+mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type,  WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
+    (void) frame;
+    (void) request;
+    (void) user_data;
+
+    /* If we can display it, let's display it... */
+    if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
+        webkit_web_policy_decision_use (policy_decision);
+        return TRUE;
+    }
+
+    /* ...everything we can't displayed is downloaded */
+    webkit_web_policy_decision_download (policy_decision);
+    return TRUE;
+}
+
 WebKitWebView*
 create_web_view_cb (WebKitWebView  *web_view, WebKitWebFrame *frame, gpointer user_data) {
     (void) web_view;
@@ -336,23 +453,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);
 }
@@ -428,6 +549,8 @@ load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
     (void) page;
     (void) frame;
     (void) data;
+    uzbl.gui.sbar.load_progress = 0;
+    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, "");
 }
@@ -443,7 +566,6 @@ load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
         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);
 }
@@ -481,7 +603,6 @@ VIEWFUNC(go_forward)
 #undef VIEWFUNC
 
 /* -- 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}              },
@@ -500,12 +621,23 @@ static struct {char *name; Command command[2];} cmdlist[] =
     { "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}             },
+    //{ "get",                {get_var, NOSPLIT}             },
+    { "bind",               {act_bind, NOSPLIT}            },
+    { "dump_config",        {act_dump_config, 0}           },
+    { "keycmd",             {keycmd, NOSPLIT}              },
+    { "keycmd_nl",          {keycmd_nl, NOSPLIT}           },
+    { "keycmd_bs",          {keycmd_bs, 0}                 },
+    { "chain",              {chain, 0}                     },
+    { "print",              {print, NOSPLIT}               }
 };
 
 static void
@@ -548,6 +680,42 @@ file_exists (const char * filename) {
 }
 
 static void
+set_var(WebKitWebView *page, GArray *argv) {
+    (void) page;
+    gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
+    gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
+    set_var_value(g_strstrip(split[0]), value);
+    g_free(value);
+    g_strfreev(split);
+}
+
+static void
+print(WebKitWebView *page, GArray *argv) {
+    (void) page;
+    gchar* buf;
+
+    buf = expand_vars(argv_idx(argv, 0));
+    puts(buf);
+    g_free(buf);
+}
+
+static void
+act_bind(WebKitWebView *page, GArray *argv) {
+    (void) page;
+    gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
+    gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
+    add_binding(g_strstrip(split[0]), value);
+    g_free(value);
+    g_strfreev(split);
+}
+
+
+static void
+act_dump_config() {
+    dump_config();
+}
+
+static void
 toggle_insert_mode(WebKitWebView *page, GArray *argv) {
     (void)page;
 
@@ -568,7 +736,11 @@ static void
 load_uri (WebKitWebView *web_view, GArray *argv) {
     if (argv_idx(argv, 0)) {
         GString* newuri = g_string_new (argv_idx(argv, 0));
-        if (g_strrstr (argv_idx(argv, 0), "://") == NULL)
+        if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
+            run_js(web_view, argv);
+            return;
+        }
+        if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
             g_string_prepend (newuri, "http://");
         /* if we do handle cookies, ask our handler for them */
         webkit_web_view_load_uri (web_view, newuri->str);
@@ -644,6 +816,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);
@@ -663,6 +842,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;
@@ -745,7 +963,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;
@@ -758,14 +976,18 @@ expand_template(const char *template) {
          token = g_scanner_get_next_token(uzbl.scan);
 
          if(token == G_TOKEN_SYMBOL) {
-             sym = (int)g_scanner_cur_value(uzbl.scan).v_symbol;
+             sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
              switch(sym) {
                  case SYM_URI:
-                     buf = uzbl.state.uri?
-                         g_markup_printf_escaped("%s", uzbl.state.uri) :
-                         g_strdup("");
-                     g_string_append(ret, buf);
-                     g_free(buf);
+                     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);
@@ -778,39 +1000,52 @@ expand_template(const char *template) {
                      g_free(buf);
                      break;
                  case SYM_TITLE:
-                     buf = uzbl.gui.main_title?
-                         g_markup_printf_escaped("%s", uzbl.gui.main_title) :
-                         g_strdup("");
-                     g_string_append(ret, buf);
-                     g_free(buf);
+                     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:
-                     buf = uzbl.state.selected_url?
-                         g_markup_printf_escaped("%s", uzbl.state.selected_url) :
-                         g_strdup("");
-                     g_string_append(ret, buf);
-                     g_free(buf);
-                    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);
+                             uzbl.state.instance_name?uzbl.state.instance_name:buf);
                      g_free(buf);
                      break;
                  case SYM_KEYCMD:
-                     buf = uzbl.state.keycmd->str?
-                         g_markup_printf_escaped("%s", uzbl.state.keycmd->str) :
-                         g_strdup("");
-                     g_string_append(ret, buf);
-                     g_free(buf);
+                     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:
@@ -884,7 +1119,7 @@ sharg_append(GArray *a, const gchar *str) {
 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
 static gboolean
 run_command (const gchar *command, const guint npre, const gchar **args,
-             const gboolean sync, char **stdout) {
+             const gboolean sync, char **output_stdout) {
    //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
     GError *err = NULL;
     
@@ -907,10 +1142,13 @@ run_command (const gchar *command, const guint npre, const gchar **args,
         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 (*output_stdout) *output_stdout = strfree(*output_stdout);
+        
+        result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
+                              NULL, NULL, output_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:");
@@ -922,6 +1160,9 @@ run_command (const gchar *command, const guint npre, const gchar **args,
         g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
         printf("%s\n", s->str);
         g_string_free(s, TRUE);
+        if(output_stdout) {
+            printf("Stdout: %s\n", *output_stdout);
+        }
     }
     if (err) {
         g_printerr("error on run_command: %s\n", err->message);
@@ -979,6 +1220,15 @@ spawn(WebKitWebView *web_view, GArray *argv) {
 }
 
 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
 spawn_sh(WebKitWebView *web_view, GArray *argv) {
     (void)web_view;
     if (!uzbl.behave.shell_cmd) {
@@ -1000,6 +1250,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;
 
@@ -1023,34 +1295,6 @@ parse_command(const char *cmd, const char *param) {
         g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
 }
 
-/* command parser */
-static void
-setup_regex() {
-    uzbl.comm.get_regex  = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
-            G_REGEX_OPTIMIZE, 0, NULL);
-    uzbl.comm.set_regex  = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
-            G_REGEX_OPTIMIZE, 0, NULL);
-    uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
-            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) {
-    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;
-}
-
 static void
 set_proxy_url() {
     SoupURI *suri;
@@ -1071,6 +1315,16 @@ set_proxy_url() {
 }
 
 static void
+set_icon() {
+    if(file_exists(uzbl.gui.icon)) {
+        gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
+    } else {
+        g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
+    }
+    g_free (uzbl.gui.icon);
+}
+
+static void
 cmd_load_uri() {
     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
     g_array_append_val (a, uzbl.state.uri);
@@ -1109,9 +1363,14 @@ cmd_http_debug() {
             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 = webkit_web_view_get_settings(uzbl.gui.web_view);
+    WebKitWebSettings *ws = view_settings();
     if (uzbl.behave.font_size > 0) {
         g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
     }
@@ -1126,15 +1385,100 @@ cmd_font_size() {
 }
 
 static void
+cmd_zoom_level() {
+    webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
+}
+
+static void
 cmd_disable_plugins() {
-    WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
-    g_object_set (G_OBJECT(ws), "enable-plugins", !uzbl.behave.disable_plugins, NULL);
+    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() {
-    WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
-    g_object_set (G_OBJECT(ws), "minimum-font-size", uzbl.behave.minimum_font_size, NULL);
+    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);
+    /* pitfall: doesn't handle chain actions; must the sync_ action manually */
+    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
@@ -1148,6 +1492,14 @@ cmd_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;
@@ -1171,7 +1523,7 @@ cmd_useragent() {
         g_free (uzbl.net.useragent);
         uzbl.net.useragent = NULL;
     } else {
-        gchar *ua = expand_template(uzbl.net.useragent);
+        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);
@@ -1204,15 +1556,24 @@ static gboolean
 set_var_value(gchar *name, gchar *val) {
     uzbl_cmdprop *c = NULL;
     char *endp = NULL;
+    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 = g_strdup(val);
+            *c->ptr = buf;
         } else if(c->type == TYPE_INT) {
-            int *ip = GPOINTER_TO_INT(c->ptr);
-            *ip = (int)strtoul(val, &endp, 10);
+            int *ip = (int *)c->ptr;
+            buf = expand_vars(val);
+            *ip = (int)strtoul(buf, &endp, 10);
+            g_free(buf);
+        } else if (c->type == TYPE_FLOAT) {
+            float *fp = (float *)c->ptr;
+            buf = expand_vars(val);
+            *fp = strtod(buf, &endp);
+            g_free(buf);
         }
 
         /* invoke a command specific function */
@@ -1222,82 +1583,56 @@ set_var_value(gchar *name, gchar *val) {
 }
 
 static void
-runcmd(WebKitWebView* page, GArray *argv) {
-    (void) page;
-    parse_cmd_line(argv_idx(argv, 0));
+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(g_strdup(tokens[2]));
-            set_var_value(tokens[1], value);
-            g_strfreev(tokens);
-            g_free(value);
-        }
-        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
-            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_strfreev(tokens);
-            g_free(value);
+    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]);
-    }
-    /* 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);
-        }
-        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);
-            if (g_strstr_len(ctl_line, 7, "n") || g_strstr_len(ctl_line, 7, "N"))
-                run_keycmd(TRUE);
-            update_title();
-            g_strfreev(tokens);
+        else {
+            set_timeout(b->html_timeout);
+            g_string_append(b->html_buffer, ctl_line);
         }
     }
-    /* Comments */
-    else if(   (ctl_line[0] == '#')
+    else if((ctl_line[0] == '#') /* Comments */
             || (ctl_line[0] == ' ')
             || (ctl_line[0] == '\n'))
         ; /* ignore these lines */
-    else
-        printf("Command not understood (%s)\n", ctl_line);
-
-    return;
+    else { /* parse a command */
+        gchar *ctlstrip;
+        gchar **tokens = NULL;
+        len = strlen(ctl_line);
+
+        if (ctl_line[len - 1] == '\n') /* strip trailing newline */
+            ctlstrip = g_strndup(ctl_line, len - 1);
+        else ctlstrip = g_strdup(ctl_line);
+
+        tokens = g_strsplit(ctlstrip, " ", 2);
+        parse_command(tokens[0], tokens[1]);
+        g_free(ctlstrip);
+        g_strfreev(tokens);
+    }
 }
 
 static gchar*
@@ -1531,12 +1866,13 @@ update_title (void) {
 
     if (b->show_status) {
         if (b->title_format_short) {
-            parsed = expand_template(b->title_format_short);
-            gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
+            parsed = expand_template(b->title_format_short, FALSE);
+            if (uzbl.gui.main_window)
+                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);
         }
@@ -1544,12 +1880,14 @@ update_title (void) {
             GdkColor color;
             gdk_color_parse (b->status_background, &color);
             //labels and hboxes do not draw their own background.  applying this on the window is ok as we the statusbar is the only affected widget.  (if not, we could also use GtkEventBox)
-            gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
+            if (uzbl.gui.main_window)
+                gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
         }
     } else {
         if (b->title_format_long) {
-            parsed = expand_template(b->title_format_long);
-            gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
+            parsed = expand_template(b->title_format_long, FALSE);
+            if (uzbl.gui.main_window)
+                gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
             g_free(parsed);
         }
     }
@@ -1579,6 +1917,7 @@ key_press_cb (GtkWidget* window, GdkEventKey* event)
     if (event->keyval == GDK_Escape) {
         g_string_truncate(uzbl.state.keycmd, 0);
         update_title();
+        dehilight(uzbl.gui.web_view, NULL);
         return TRUE;
     }
 
@@ -1598,10 +1937,8 @@ key_press_cb (GtkWidget* window, GdkEventKey* event)
         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))
@@ -1617,57 +1954,58 @@ key_press_cb (GtkWidget* window, 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;
@@ -1689,6 +2027,7 @@ create_browser () {
     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);
+    g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
 
     return scrolled_window;
 }
@@ -1708,6 +2047,7 @@ create_mainbar () {
     gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
     gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
     gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
+    g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
     return g->mainbar;
 }
 
@@ -1722,26 +2062,115 @@ GtkWidget* create_window () {
     return window;
 }
 
+static
+GtkPlug* create_plug () {
+    GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
+    g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
+    g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
+
+    return plug;
+}
+
+
+static gchar**
+inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
+    /*
+      If actname is one that calls an external command, this function will inject
+      newargs in front of the user-provided args in that command line.  They will
+      come become after the body of the script (in sh) or after the name of
+      the command to execute (in spawn).
+      i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
+      span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
+
+      The return value consist of two strings: the action (sh, ...) and its args.
+
+      If act is not one that calls an external command, then the given action merely
+      gets duplicated.
+    */
+    GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
+    gchar *actdup = g_strdup(actname);
+    g_array_append_val(rets, actdup);
+
+    if ((g_strcmp0(actname, "spawn") == 0) ||
+        (g_strcmp0(actname, "sh") == 0) ||
+        (g_strcmp0(actname, "sync_spawn") == 0) ||
+        (g_strcmp0(actname, "sync_sh") == 0)) {
+        guint i;
+        GString *a = g_string_new("");
+        gchar **spawnparts = split_quoted(origargs, FALSE);
+        g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
+        if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
+
+        for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
+            if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
+
+        g_array_append_val(rets, a->str);
+        g_string_free(a, FALSE);
+        g_strfreev(spawnparts);
+    } else {
+        gchar *origdup = g_strdup(origargs);
+        g_array_append_val(rets, origdup);
+    }
+    return (gchar**)g_array_free(rets, FALSE);
+}
+
 static void
 run_handler (const gchar *act, const gchar *args) {
+    /* Consider this code a temporary hack to make the handlers usable.
+       In practice, all this splicing, injection, and reconstruction is
+       inefficient, annoying and hard to manage.  Potential pitfalls arise
+       when the handler specific args 1) are not quoted  (the handler
+       callbacks should take care of this)  2) are quoted but interfere
+       with the users' own quotation.  A more ideal solution is
+       to refactor parse_command so that it doesn't just take a string
+       and execute it; rather than that, we should have a function which
+       returns the argument vector parsed from the string.  This vector
+       could be modified (e.g. insert additional args into it) before
+       passing it to the next function that actually executes it.  Though
+       it still isn't perfect for chain actions..  will reconsider & re-
+       factor when I have the time. -duc */
+
     char **parts = g_strsplit(act, " ", 2);
     if (!parts) return;
-    else if ((g_strcmp0(parts[0], "spawn") == 0)
-             || (g_strcmp0(parts[0], "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);
-        g_string_free (a, TRUE);
-        g_strfreev (spawnparts);
-    } else
-        parse_command(parts[0], parts[1]);
-    g_strfreev (parts);
+    if (g_strcmp0(parts[0], "chain") == 0) {
+        GString *newargs = g_string_new("");
+        gchar **chainparts = split_quoted(parts[1], FALSE);
+        
+        /* for every argument in the chain, inject the handler args
+           and make sure the new parts are wrapped in quotes */
+        gchar **cp = chainparts;
+        gchar quot = '\'';
+        gchar *quotless = NULL;
+        gchar **spliced_quotless = NULL; // sigh -_-;
+        gchar **inpart = NULL;
+        
+        while (*cp) {
+            if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
+                quot = **cp;
+                quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
+            } else quotless = g_strdup(*cp);
+
+            spliced_quotless = g_strsplit(quotless, " ", 2);
+            inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
+            g_strfreev(spliced_quotless);
+            
+            g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
+            g_free(quotless);
+            g_strfreev(inpart);
+            cp++;
+        }
+
+        parse_command(parts[0], &(newargs->str[1]));
+        g_string_free(newargs, TRUE);
+        g_strfreev(chainparts);
+        
+    } else {
+        gchar **inparts = inject_handler_args(parts[0], parts[1], args);
+        parse_command(inparts[0], inparts[1]);
+        g_free(inparts[0]);
+        g_free(inparts[1]);
+    }
+    g_strfreev(parts);
 }
 
 static void
@@ -1757,6 +2186,8 @@ 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_remove (uzbl.bindings, key))
+        g_warning ("Overwriting existing binding for \"%s\"", key);
     g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
     g_strfreev(parts);
 }
@@ -1765,8 +2196,7 @@ static gchar*
 get_xdg_var (XDG_Var xdg) {
     const gchar* actual_value = getenv (xdg.environmental);
     const gchar* home         = getenv ("HOME");
-
-    gchar* return_value = str_replace ("~", home, actual_value);
+    gchar* return_value;
 
     if (! actual_value || strcmp (actual_value, "") == 0) {
         if (xdg.default_value) {
@@ -1774,7 +2204,10 @@ get_xdg_var (XDG_Var xdg) {
         } else {
             return_value = NULL;
         }
+    } else {
+        return_value = str_replace("~", home, actual_value);
     }
+
     return return_value;
 }
 
@@ -1840,29 +2273,30 @@ settings_init () {
             printf ("No configuration file loaded.\n");
     }
 
-    g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
+    g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
 }
 
 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
     (void) session;
     (void) user_data;
-    if (!uzbl.behave.cookie_handler) return;
+    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_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 && strcmp (uzbl.comm.sync_stdout, "") != 0) {
+        char *p = strchr(uzbl.comm.sync_stdout, '\n' );
+        if ( p != NULL ) *p = '\0';
+        soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
     }
-    g_free (action);
-    g_array_free(a, TRUE);
+    if (uzbl.comm.sync_stdout)
+        uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
+        
+    g_string_free(s, TRUE);
 }
 
 static void
@@ -1872,21 +2306,138 @@ 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_detach_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), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
+    g_signal_connect (G_OBJECT (g->inspector), "finished", 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);
@@ -1901,6 +2452,10 @@ main (int argc, char* argv[]) {
     g_option_context_add_group (context, gtk_get_option_group (TRUE));
     g_option_context_parse (context, &argc, &argv, NULL);
     g_option_context_free(context);
+    
+    gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
+    gboolean verbose_override = uzbl.state.verbose;
+
     /* initialize hash table */
     uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
 
@@ -1911,6 +2466,9 @@ 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");
@@ -1919,7 +2477,16 @@ main (int argc, char* argv[]) {
     uzbl.gui.sbar.progress_u = g_strdup("ยท");
     uzbl.gui.sbar.progress_w = 10;
 
-    setup_regex();
+    /* 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_scanner();
     commands_hash ();
     make_var_to_name_hash();
@@ -1933,17 +2500,25 @@ main (int argc, char* argv[]) {
     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
 
-    uzbl.gui.main_window = create_window ();
-    gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
-
+    if (uzbl.state.socket_id) {
+        uzbl.gui.plug = create_plug ();
+        gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
+        gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
+    } else {
+        uzbl.gui.main_window = create_window ();
+        gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
+        gtk_widget_show_all (uzbl.gui.main_window);
+        uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
+    }
 
     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
-    gtk_widget_show_all (uzbl.gui.main_window);
-    uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
 
     if (uzbl.state.verbose) {
         printf("Uzbl start location: %s\n", argv[0]);
-        printf("window_id %i\n",(int) uzbl.xwin);
+        if (uzbl.state.socket_id)
+            printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
+        else
+            printf("window_id %i\n",(int) uzbl.xwin);
         printf("pid %i\n", getpid ());
         printf("name: %s\n", uzbl.state.instance_name);
     }
@@ -1961,14 +2536,19 @@ main (int argc, char* argv[]) {
     else
         update_title();
 
+    /* WebInspector */
+    set_up_inspector();
+
     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);
-    }
+    if (verbose_override > uzbl.state.verbose)
+        uzbl.state.verbose = verbose_override;
+    
+    if (uri_override) {
+        set_var_value("uri", uri_override);
+        g_free(uri_override);
+    } else if (uzbl.state.uri)
+        cmd_load_uri(uzbl.gui.web_view, NULL);
 
     gtk_main ();
     clean_up();