tidy up move to expand()
[uzbl-mobile] / uzbl.c
diff --git a/uzbl.c b/uzbl.c
index a1f1bab..9e833d6 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>
@@ -44,6 +45,7 @@
 #include <sys/time.h>
 #include <webkit/webkit.h>
 #include <libsoup/soup.h>
+#include <JavaScriptCore/JavaScript.h>
 
 #include <stdio.h>
 #include <string.h>
 #include "uzbl.h"
 #include "config.h"
 
-static Uzbl uzbl;
-typedef void (*Command)(WebKitWebView*, GArray *argv);
+Uzbl uzbl;
 
 
 
 /* commandline arguments (set initial values for the state variables) */
-static const 
+static const
 GOptionEntry entries[] =
 {
-    { "uri",     'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
+    { "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,
+    { "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",     '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",   '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" },
+    { "geometry", 'g', 0, G_OPTION_ARG_STRING, &uzbl.gui.geometry, 
+        "Set window geometry (format: WIDTHxHEIGHT+-X+-Y)", "GEOMETRY" },
+    { "version",  'V', 0, G_OPTION_ARG_NONE, &uzbl.behave.print_version,
+        "Print the version and exit", NULL },
     { NULL,      0, 0, 0, NULL, NULL, NULL }
 };
 
@@ -80,78 +87,98 @@ typedef const struct {
     void **ptr;
     int type;
     int dump;
+    int writeable;
     void (*func)(void);
 } uzbl_cmdprop;
 
 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
 
-/* 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 }
+/* abbreviations to help keep the table's width humane */
+#define PTR_V(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .writeable = 1, .func = fun }
+#define PTR_C(var, t,    fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = 0, .writeable = 0, .func = fun }
 
 const struct {
     char *name;
     uzbl_cmdprop cp;
 } var_name_to_ptr[] = {
-/*    variable name         pointer to variable in code          type  dump callback function    */
-/*  --------------------------------------------------------------------------------------- */
-    { "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)},
-    { "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)},
+/*    variable name         pointer to variable in code            type  dump callback function    */
+/*  ---------------------------------------------------------------------------------------------- */
+    { "uri",                 PTR_V(uzbl.state.uri,                  STR,  1,   cmd_load_uri)},
+    { "verbose",             PTR_V(uzbl.state.verbose,              INT,  1,   NULL)},
+    { "mode",                PTR_V(uzbl.behave.mode,                INT,  0,   NULL)},
+    { "inject_html",         PTR_V(uzbl.behave.inject_html,         STR,  0,   cmd_inject_html)},
+    { "base_url",            PTR_V(uzbl.behave.base_url,            STR,  1,   NULL)},
+    { "html_endmarker",      PTR_V(uzbl.behave.html_endmarker,      STR,  1,   NULL)},
+    { "html_mode_timeout",   PTR_V(uzbl.behave.html_timeout,        INT,  1,   NULL)},
+    { "keycmd",              PTR_V(uzbl.state.keycmd,               STR,  1,   set_keycmd)},
+    { "status_message",      PTR_V(uzbl.gui.sbar.msg,               STR,  1,   update_title)},
+    { "show_status",         PTR_V(uzbl.behave.show_status,         INT,  1,   cmd_set_status)},
+    { "status_top",          PTR_V(uzbl.behave.status_top,          INT,  1,   move_statusbar)},
+    { "status_format",       PTR_V(uzbl.behave.status_format,       STR,  1,   update_title)},
+    { "status_pbar_done",    PTR_V(uzbl.gui.sbar.progress_s,        STR,  1,   update_title)},
+    { "status_pbar_pending", PTR_V(uzbl.gui.sbar.progress_u,        STR,  1,   update_title)},
+    { "status_pbar_width",   PTR_V(uzbl.gui.sbar.progress_w,        INT,  1,   update_title)},
+    { "status_background",   PTR_V(uzbl.behave.status_background,   STR,  1,   update_title)},
+    { "insert_indicator",    PTR_V(uzbl.behave.insert_indicator,    STR,  1,   update_title)},
+    { "command_indicator",   PTR_V(uzbl.behave.cmd_indicator,       STR,  1,   update_title)},
+    { "title_format_long",   PTR_V(uzbl.behave.title_format_long,   STR,  1,   update_title)},
+    { "title_format_short",  PTR_V(uzbl.behave.title_format_short,  STR,  1,   update_title)},
+    { "icon",                PTR_V(uzbl.gui.icon,                   STR,  1,   set_icon)},
+    { "insert_mode",         PTR_V(uzbl.behave.insert_mode,         INT,  1,   set_mode_indicator)},
+    { "always_insert_mode",  PTR_V(uzbl.behave.always_insert_mode,  INT,  1,   cmd_always_insert_mode)},
+    { "reset_command_mode",  PTR_V(uzbl.behave.reset_command_mode,  INT,  1,   NULL)},
+    { "modkey",              PTR_V(uzbl.behave.modkey,              STR,  1,   cmd_modkey)},
+    { "load_finish_handler", PTR_V(uzbl.behave.load_finish_handler, STR,  1,   NULL)},
+    { "load_start_handler",  PTR_V(uzbl.behave.load_start_handler,  STR,  1,   NULL)},
+    { "load_commit_handler", PTR_V(uzbl.behave.load_commit_handler, STR,  1,   NULL)},
+    { "history_handler",     PTR_V(uzbl.behave.history_handler,     STR,  1,   NULL)},
+    { "download_handler",    PTR_V(uzbl.behave.download_handler,    STR,  1,   NULL)},
+    { "cookie_handler",      PTR_V(uzbl.behave.cookie_handler,      STR,  1,   cmd_cookie_handler)},
+    { "new_window",          PTR_V(uzbl.behave.new_window,          STR,  1,   cmd_new_window)},
+    { "fifo_dir",            PTR_V(uzbl.behave.fifo_dir,            STR,  1,   cmd_fifo_dir)},
+    { "socket_dir",          PTR_V(uzbl.behave.socket_dir,          STR,  1,   cmd_socket_dir)},
+    { "http_debug",          PTR_V(uzbl.behave.http_debug,          INT,  1,   cmd_http_debug)},
+    { "shell_cmd",           PTR_V(uzbl.behave.shell_cmd,           STR,  1,   NULL)},
+    { "proxy_url",           PTR_V(uzbl.net.proxy_url,              STR,  1,   set_proxy_url)},
+    { "max_conns",           PTR_V(uzbl.net.max_conns,              INT,  1,   cmd_max_conns)},
+    { "max_conns_host",      PTR_V(uzbl.net.max_conns_host,         INT,  1,   cmd_max_conns_host)},
+    { "useragent",           PTR_V(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}}
+    { "zoom_level",          PTR_V(uzbl.behave.zoom_level,          FLOAT,1,   cmd_zoom_level)},
+    { "font_size",           PTR_V(uzbl.behave.font_size,           INT,  1,   cmd_font_size)},
+    { "monospace_size",      PTR_V(uzbl.behave.monospace_size,      INT,  1,   cmd_font_size)},
+    { "minimum_font_size",   PTR_V(uzbl.behave.minimum_font_size,   INT,  1,   cmd_minimum_font_size)},
+    { "disable_plugins",     PTR_V(uzbl.behave.disable_plugins,     INT,  1,   cmd_disable_plugins)},
+    { "disable_scripts",     PTR_V(uzbl.behave.disable_scripts,     INT,  1,   cmd_disable_scripts)},
+    { "autoload_images",     PTR_V(uzbl.behave.autoload_img,        INT,  1,   cmd_autoload_img)},
+    { "autoshrink_images",   PTR_V(uzbl.behave.autoshrink_img,      INT,  1,   cmd_autoshrink_img)},
+    { "enable_spellcheck",   PTR_V(uzbl.behave.enable_spellcheck,   INT,  1,   cmd_enable_spellcheck)},
+    { "enable_private",      PTR_V(uzbl.behave.enable_private,      INT,  1,   cmd_enable_private)},
+    { "print_backgrounds",   PTR_V(uzbl.behave.print_bg,            INT,  1,   cmd_print_bg)},
+    { "stylesheet_uri",      PTR_V(uzbl.behave.style_uri,           STR,  1,   cmd_style_uri)},
+    { "resizable_text_areas",PTR_V(uzbl.behave.resizable_txt,       INT,  1,   cmd_resizable_txt)},
+    { "default_encoding",    PTR_V(uzbl.behave.default_encoding,    STR,  1,   cmd_default_encoding)},
+    { "enforce_96_dpi",      PTR_V(uzbl.behave.enforce_96dpi,       INT,  1,   cmd_enforce_96dpi)},
+    { "caret_browsing",      PTR_V(uzbl.behave.caret_browsing,      INT,  1,   cmd_caret_browsing)},
+
+  /* constants (not dumpable or writeable) */
+    { "WEBKIT_MAJOR",        PTR_C(uzbl.info.webkit_major,          INT,       NULL)},
+    { "WEBKIT_MINOR",        PTR_C(uzbl.info.webkit_minor,          INT,       NULL)},
+    { "WEBKIT_MICRO",        PTR_C(uzbl.info.webkit_micro,          INT,       NULL)},
+    { "ARCH_UZBL",           PTR_C(uzbl.info.arch,                  STR,       NULL)},
+    { "COMMIT",              PTR_C(uzbl.info.commit,                STR,       NULL)},
+    { "LOAD_PROGRESS",       PTR_C(uzbl.gui.sbar.load_progress,     INT,       NULL)},
+    { "LOAD_PROGRESSBAR",    PTR_C(uzbl.gui.sbar.progress_bar,      STR,       NULL)},
+    { "TITLE",               PTR_C(uzbl.gui.main_title,             STR,       NULL)},
+    { "SELECTED_URI",        PTR_C(uzbl.state.selected_url,         STR,       NULL)},
+    { "MODE",                PTR_C(uzbl.gui.sbar.mode_indicator,    STR,       NULL)},
+    { "NAME",                PTR_C(uzbl.state.instance_name,        STR,       NULL)},
+
+    { NULL,                  {.ptr = NULL, .type = TYPE_INT, .dump = 0, .writeable = 0, .func = NULL}}
 }, *n2v_p = var_name_to_ptr;
 
+
 const struct {
     char *key;
     guint mask;
@@ -176,8 +203,9 @@ const struct {
 };
 
 
-/* construct a hash from the var_name_to_ptr array for quick access */
-static void
+/* construct a hash from the var_name_to_ptr and the const_name_to_ptr array
+ * for quick access */
+void
 make_var_to_name_hash() {
     uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
     while(n2v_p->name) {
@@ -187,12 +215,43 @@ make_var_to_name_hash() {
 }
 
 /* --- UTILITY FUNCTIONS --- */
-static gchar *
-expand_vars(char *s) {
+
+enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS, EXP_ESCAPE};
+static guint
+get_exp_type(gchar *s) {
+    /* variables */
+    if(*(s+1) == '(')
+        return EXP_EXPR;
+    else if(*(s+1) == '{')
+        return EXP_BRACED_VAR;
+    else if(*(s+1) == '<')
+        return EXP_JS;
+    else if(*(s+1) == '[')
+        return EXP_ESCAPE;
+    else
+        return EXP_SIMPLE_VAR;
+
+return EXP_ERR;
+}
+
+/*
+ * recurse == 1: don't expand '@(command)@'
+ * recurse == 2: don't expand '@<java script>@'
+ */
+gchar *
+expand(char *s, guint recurse) {
     uzbl_cmdprop *c;
+    guint etype;
     char upto = ' ';
-    char ret[256],  *vend;
+    char *end_simple_var = "^°!\"§$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]¹²³¼½";
+    char str_end[2];
+    char ret[4096];
+    char *vend = NULL;
+    GError *err = NULL;
+    gchar *cmd_stdout = NULL;
+    gchar *mycmd = NULL;
     GString *buf = g_string_new("");
+    GString *js_ret = g_string_new("");
 
     while(*s) {
         switch(*s) {
@@ -200,35 +259,119 @@ expand_vars(char *s) {
                 g_string_append_c(buf, *++s);
                 s++;
                 break;
+
             case '@':
-                if(*(s+1) == '{') {
-                    upto = '}'; s++;
-                }
+                etype = get_exp_type(s);
                 s++;
-                if( (vend = strchr(s, upto)) ||
-                        (vend = strchr(s, '\0')) ) {
+
+                switch(etype) {
+                    case EXP_SIMPLE_VAR:
+                        vend = strpbrk(s, end_simple_var);
+                        if(!vend) vend = strchr(s, '\0');
+                        break;
+                    case EXP_BRACED_VAR:
+                        s++; upto = '}';
+                        vend = strchr(s, upto);
+                        if(!vend) vend = strchr(s, '\0');
+                        break;
+                    case EXP_EXPR:
+                        s++;
+                        strcpy(str_end, ")@");
+                        str_end[2] = '\0';
+                        vend = strstr(s, str_end);
+                        if(!vend) vend = strchr(s, '\0');
+                        break;
+                    case EXP_JS:
+                        s++;
+                        strcpy(str_end, ">@");
+                        str_end[2] = '\0';
+                        vend = strstr(s, str_end);
+                        if(!vend) vend = strchr(s, '\0');
+                        break;
+                    case EXP_ESCAPE:
+                        s++;
+                        strcpy(str_end, "]@");
+                        str_end[2] = '\0';
+                        vend = strstr(s, str_end);
+                        if(!vend) vend = strchr(s, '\0');
+                        break;
+                }
+
+                if(vend) {
                     strncpy(ret, s, vend-s);
                     ret[vend-s] = '\0';
+                }
+
+                if(etype == EXP_SIMPLE_VAR ||
+                   etype == EXP_BRACED_VAR) {
                     if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
-                        if(c->type == TYPE_STR)
+                        if(c->type == TYPE_STR && *c->ptr != NULL) {
                             g_string_append(buf, (gchar *)*c->ptr);
-                        else if(c->type == TYPE_INT) {
-                            char *b = itos((int)*c->ptr);
+                        } else if(c->type == TYPE_INT) {
+                            char *b = itos((uintptr_t)*c->ptr);
                             g_string_append(buf, b);
                             g_free(b);
                         }
                     }
-                    if(upto == ' ') s = vend;
-                    else s = vend+1;
-                    upto = ' ';
+
+                    if(etype == EXP_SIMPLE_VAR)
+                        s = vend;
+                    else
+                        s = vend+1;
+                }
+                else if(recurse != 1 &&
+                        etype == EXP_EXPR) {
+                    mycmd = expand(ret, 1);
+                    g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
+                    g_free(mycmd);
+
+                    if (err) {
+                        g_printerr("error on running command: %s\n", err->message);
+                        g_error_free (err);
+                    }
+                    else if (*cmd_stdout) {
+                        int len = strlen(cmd_stdout);
+
+                        if(cmd_stdout[len-1] == '\n')
+                            cmd_stdout[--len] = 0; /* strip trailing newline */
+
+                        g_string_append(buf, cmd_stdout);
+                        g_free(cmd_stdout);
+                    }
+                    s = vend+2;
+                }
+                else if(recurse != 2 &&
+                        etype == EXP_JS) {
+                    mycmd = expand(ret, 2);
+                    eval_js(uzbl.gui.web_view, mycmd, js_ret);
+                    g_free(mycmd);
+
+                    if(js_ret->str) {
+                        g_string_append(buf, js_ret->str);
+                        g_string_free(js_ret, TRUE);
+                        js_ret = g_string_new("");
+                    }
+                    s = vend+2;
+                }
+                else if(etype == EXP_ESCAPE) {
+                    mycmd = expand(ret, 0);
+                    char *escaped = g_markup_escape_text(mycmd, strlen(mycmd));
+
+                    g_string_append(buf, escaped);
+
+                    g_free(escaped);
+                    g_free(mycmd);
+                    s = vend+2;
                 }
                 break;
+
             default:
                 g_string_append_c(buf, *s);
                 s++;
                 break;
         }
     }
+    g_string_free(js_ret, TRUE);
     return g_string_free(buf, FALSE);
 }
 
@@ -265,9 +408,9 @@ read_file_by_line (gchar *path) {
     gsize len;
     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) {
             const gchar* val = g_strdup (readbuf);
@@ -275,12 +418,12 @@ read_file_by_line (gchar *path) {
             g_free (readbuf);
             i ++;
         }
-        
+
         g_io_channel_unref (chan);
     } else {
         fprintf(stderr, "File '%s' not be read.\n", path);
     }
-    
+
     return lines;
 }
 
@@ -289,7 +432,7 @@ gchar* parseenv (char* string) {
     extern char** environ;
     gchar* tmpstr = NULL;
     int i = 0;
-    
+
 
     while (environ[i] != NULL) {
         gchar** env = g_strsplit (environ[i], "=", 2);
@@ -332,12 +475,12 @@ clean_up(void) {
         unlink (uzbl.comm.socket_path);
 
     g_free(uzbl.state.executable_path);
-    g_string_free(uzbl.state.keycmd, TRUE);
+    g_free(uzbl.state.keycmd);
     g_hash_table_destroy(uzbl.bindings);
     g_hash_table_destroy(uzbl.behave.commands);
 }
 
-/* used for html_mode_timeout 
+/* used for html_mode_timeout
  * be sure to extend this function to use
  * more timers if needed in other places
 */
@@ -387,8 +530,8 @@ new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequ
     const gchar* uri = webkit_network_request_get_uri (request);
     if (uzbl.state.verbose)
         printf("New window requested -> %s \n", uri);
-    new_window_load_uri(uri);
-    return (FALSE);
+    webkit_web_policy_decision_use(policy_decision);
+    return TRUE;
 }
 
 static gboolean
@@ -441,40 +584,66 @@ download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
 /* scroll a bar in a given direction */
 static void
 scroll (GtkAdjustment* bar, GArray *argv) {
-    gdouble amount;
     gchar *end;
+    gdouble max_value;
 
-    amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
-    if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
-    gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
+    gdouble page_size = gtk_adjustment_get_page_size(bar);
+    gdouble value = gtk_adjustment_get_value(bar);
+    gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
+
+    if (*end == '%')
+        value += page_size * amount * 0.01;
+    else
+        value += amount;
+
+    max_value = gtk_adjustment_get_upper(bar) - page_size;
+
+    if (value > max_value)
+        value = max_value; /* don't scroll past the end of the page */
+
+    gtk_adjustment_set_value (bar, value);
 }
 
 static void
-scroll_begin(WebKitWebView* page, GArray *argv) {
-    (void) page; (void) argv;
+scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
+    (void) page; (void) argv; (void) result;
     gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
 }
 
 static void
-scroll_end(WebKitWebView* page, GArray *argv) {
-    (void) page; (void) argv;
+scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
+    (void) page; (void) argv; (void) result;
     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) {
-    (void) page;
+scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
+    (void) page; (void) result;
     scroll(uzbl.gui.bar_v, argv);
 }
 
 static void
-scroll_horz(WebKitWebView* page, GArray *argv) {
-    (void) page;
+scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
+    (void) page; (void) result;
     scroll(uzbl.gui.bar_h, argv);
 }
 
 static void
+cmd_set_geometry() {
+    if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
+        if(uzbl.state.verbose)
+            printf("Error in geometry string: %s\n", uzbl.gui.geometry);
+    }
+    /* update geometry var with the actual geometry 
+       this is necessary as some WMs don't seem to honour
+       the above setting and we don't want to end up with
+       wrong geometry information
+    */
+    retreive_geometry();
+}
+
+static void
 cmd_set_status() {
     if (!uzbl.behave.show_status) {
         gtk_widget_hide(uzbl.gui.mainbar);
@@ -485,9 +654,19 @@ cmd_set_status() {
 }
 
 static void
-toggle_status_cb (WebKitWebView* page, GArray *argv) {
+toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
+    (void)page;
+    (void)argv;
+    (void)result;
+
+    webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
+}
+
+static void
+toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
     (void)page;
     (void)argv;
+    (void)result;
 
     if (uzbl.behave.show_status) {
         gtk_widget_hide(uzbl.gui.mainbar);
@@ -513,21 +692,25 @@ link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpoin
 }
 
 static void
-title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
+title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
     (void) web_view;
-    (void) web_frame;
-    (void) data;
+    (void) param_spec;
+    const gchar *title = webkit_web_view_get_title(web_view);
     if (uzbl.gui.main_title)
         g_free (uzbl.gui.main_title);
-    uzbl.gui.main_title = g_strdup (title);
+    uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
     update_title();
 }
 
-static void
+void
 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
     (void) page;
     (void) data;
     uzbl.gui.sbar.load_progress = progress;
+
+    g_free(uzbl.gui.sbar.progress_bar);
+    uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
+
     update_title();
 }
 
@@ -540,12 +723,18 @@ load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
         run_handler(uzbl.behave.load_finish_handler, "");
 }
 
+void clear_keycmd() {
+  g_free(uzbl.state.keycmd);
+  uzbl.state.keycmd = g_strdup("");
+}
+
 static void
 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?
+    uzbl.gui.sbar.load_progress = 0;
+    clear_keycmd(); // don't need old commands to remain on new page?
     if (uzbl.behave.load_start_handler)
         run_handler(uzbl.behave.load_start_handler, "");
 }
@@ -558,7 +747,7 @@ load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
     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;
+        set_insert_mode(uzbl.behave.always_insert_mode);
         update_title();
     }
     if (uzbl.behave.load_commit_handler)
@@ -587,7 +776,7 @@ log_history_cb () {
 
 
 /* VIEW funcs (little webkit wrappers) */
-#define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv){(void)argv; webkit_web_view_##name(page);}
+#define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
 VIEWFUNC(reload)
 VIEWFUNC(reload_bypass_cache)
 VIEWFUNC(stop_loading)
@@ -598,7 +787,7 @@ VIEWFUNC(go_forward)
 #undef VIEWFUNC
 
 /* -- command to callback/function map for things we cannot attach to any signals */
-static struct {char *name; Command command[2];} cmdlist[] =
+static struct {char *key; CommandInfo value;} cmdlist[] =
 {   /* key                   function      no_split      */
     { "back",               {view_go_back, 0}              },
     { "forward",            {view_go_forward, 0}           },
@@ -611,8 +800,9 @@ static struct {char *name; Command command[2];} cmdlist[] =
     { "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}              },
+    { "toggle_zoom_type",   {toggle_zoom_type, 0},         },
+    { "uri",                {load_uri, TRUE}               },
+    { "js",                 {run_js, TRUE}                 },
     { "script",             {run_external_js, 0}           },
     { "toggle_status",      {toggle_status_cb, 0}          },
     { "spawn",              {spawn, 0}                     },
@@ -620,19 +810,19 @@ static struct {char *name; Command command[2];} cmdlist[] =
     { "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} },
+    { "search",             {search_forward_text, TRUE}    },
+    { "search_reverse",     {search_reverse_text, TRUE}    },
     { "dehilight",          {dehilight, 0}                 },
     { "toggle_insert_mode", {toggle_insert_mode, 0}        },
-    { "set",                {set_var, NOSPLIT}             },
-    //{ "get",                {get_var, NOSPLIT}             },
-    { "bind",               {act_bind, NOSPLIT}            },
+    { "set",                {set_var, TRUE}                },
+  //{ "get",                {get_var, TRUE}                },
+    { "bind",               {act_bind, TRUE}               },
     { "dump_config",        {act_dump_config, 0}           },
-    { "keycmd",             {keycmd, NOSPLIT}              },
-    { "keycmd_nl",          {keycmd_nl, NOSPLIT}           },
+    { "keycmd",             {keycmd, TRUE}                 },
+    { "keycmd_nl",          {keycmd_nl, TRUE}              },
     { "keycmd_bs",          {keycmd_bs, 0}                 },
     { "chain",              {chain, 0}                     },
-    { "print",              {print, NOSPLIT}               }
+    { "print",              {print, TRUE}                  }
 };
 
 static void
@@ -642,7 +832,7 @@ commands_hash(void)
     uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
 
     for (i = 0; i < LENGTH(cmdlist); i++)
-        g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
+        g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
 }
 
 /* -- CORE FUNCTIONS -- */
@@ -675,28 +865,30 @@ file_exists (const char * filename) {
 }
 
 static void
-set_var(WebKitWebView *page, GArray *argv) {
-    (void) page;
+set_var(WebKitWebView *page, GArray *argv, GString *result) {
+    (void) page; (void) result;
     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);
+    if (split[0] != NULL) {
+        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;
+print(WebKitWebView *page, GArray *argv, GString *result) {
+    (void) page; (void) result;
     gchar* buf;
 
-    buf = expand_vars(argv_idx(argv, 0));
-    puts(buf);
+    buf = expand(argv_idx(argv, 0), 0);
+    g_string_assign(result, buf);
     g_free(buf);
 }
 
 static void
-act_bind(WebKitWebView *page, GArray *argv) {
-    (void) page;
+act_bind(WebKitWebView *page, GArray *argv, GString *result) {
+    (void) page; (void) result;
     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);
@@ -710,29 +902,46 @@ act_dump_config() {
     dump_config();
 }
 
+void set_keycmd() {
+    run_keycmd(FALSE);
+    update_title();
+}
+
+void set_mode_indicator() {
+    uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
+        uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
+}
+
+void set_insert_mode(gboolean mode) {
+    uzbl.behave.insert_mode = mode;
+    set_mode_indicator();
+}
+
 static void
-toggle_insert_mode(WebKitWebView *page, GArray *argv) {
-    (void)page;
+toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
+    (void) page; (void) result;
 
     if (argv_idx(argv, 0)) {
         if (strcmp (argv_idx(argv, 0), "0") == 0) {
-            uzbl.behave.insert_mode = FALSE;
+            set_insert_mode(FALSE);
         } else {
-            uzbl.behave.insert_mode = TRUE;
+            set_insert_mode(TRUE);
         }
     } else {
-        uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
+        set_insert_mode( !uzbl.behave.insert_mode );
     }
 
     update_title();
 }
 
 static void
-load_uri (WebKitWebView *web_view, GArray *argv) {
+load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
+    (void) result;
+
     if (argv_idx(argv, 0)) {
         GString* newuri = g_string_new (argv_idx(argv, 0));
         if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
-            run_js(web_view, argv);
+            run_js(web_view, argv, NULL);
             return;
         }
         if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
@@ -743,14 +952,111 @@ load_uri (WebKitWebView *web_view, GArray *argv) {
     }
 }
 
+
+/* Javascript*/
+
+static JSValueRef
+js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
+                size_t argumentCount, const JSValueRef arguments[],
+                JSValueRef* exception) {
+    (void) function;
+    (void) thisObject;
+    (void) exception;
+
+    JSStringRef js_result_string;
+    GString *result = g_string_new("");
+
+    if (argumentCount >= 1) {
+        JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
+        size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
+        char ctl_line[arg_size];
+        JSStringGetUTF8CString(arg, ctl_line, arg_size);
+
+        parse_cmd_line(ctl_line, result);
+
+        JSStringRelease(arg);
+    }
+    js_result_string = JSStringCreateWithUTF8CString(result->str);
+
+    g_string_free(result, TRUE);
+
+    return JSValueMakeString(ctx, js_result_string);
+}
+
+static JSStaticFunction js_static_functions[] = {
+    {"run", js_run_command, kJSPropertyAttributeNone},
+};
+
 static void
-run_js (WebKitWebView * web_view, GArray *argv) {
+js_init() {
+    /* This function creates the class and its definition, only once */
+    if (!uzbl.js.initialized) {
+        /* it would be pretty cool to make this dynamic */
+        uzbl.js.classdef = kJSClassDefinitionEmpty;
+        uzbl.js.classdef.staticFunctions = js_static_functions;
+
+        uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
+    }
+}
+
+
+static void
+eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
+    WebKitWebFrame *frame;
+    JSGlobalContextRef context;
+    JSObjectRef globalobject;
+    JSStringRef var_name;
+
+    JSStringRef js_script;
+    JSValueRef js_result;
+    JSStringRef js_result_string;
+    size_t js_result_size;
+
+    js_init();
+
+    frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
+    context = webkit_web_frame_get_global_context(frame);
+    globalobject = JSContextGetGlobalObject(context);
+
+    /* uzbl javascript namespace */
+    var_name = JSStringCreateWithUTF8CString("Uzbl");
+    JSObjectSetProperty(context, globalobject, var_name,
+                        JSObjectMake(context, uzbl.js.classref, NULL),
+                        kJSClassAttributeNone, NULL);
+
+    /* evaluate the script and get return value*/
+    js_script = JSStringCreateWithUTF8CString(script);
+    js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
+    if (js_result && !JSValueIsUndefined(context, js_result)) {
+        js_result_string = JSValueToStringCopy(context, js_result, NULL);
+        js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
+
+        if (js_result_size) {
+            char js_result_utf8[js_result_size];
+            JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
+            g_string_assign(result, js_result_utf8);
+        }
+
+        JSStringRelease(js_result_string);
+    }
+
+    /* cleanup */
+    JSObjectDeleteProperty(context, globalobject, var_name, NULL);
+
+    JSStringRelease(var_name);
+    JSStringRelease(js_script);
+}
+
+static void
+run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
+
     if (argv_idx(argv, 0))
-        webkit_web_view_execute_script (web_view, argv_idx(argv, 0));
+        eval_js(web_view, argv_idx(argv, 0), result);
 }
 
 static void
-run_external_js (WebKitWebView * web_view, GArray *argv) {
+run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
+    (void) result;
     if (argv_idx(argv, 0)) {
         GArray* lines = read_file_by_line (argv_idx (argv, 0));
         gchar*  js = NULL;
@@ -767,7 +1073,7 @@ run_external_js (WebKitWebView * web_view, GArray *argv) {
             i ++;
             g_free (line);
         }
-        
+
         if (uzbl.state.verbose)
             printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
 
@@ -776,7 +1082,7 @@ run_external_js (WebKitWebView * web_view, GArray *argv) {
             g_free (js);
             js = newjs;
         }
-        webkit_web_view_execute_script (web_view, js);
+        eval_js (web_view, js, result);
         g_free (js);
         g_array_free (lines, TRUE);
     }
@@ -791,7 +1097,7 @@ search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
             uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
         }
     }
-    
+
     if (uzbl.state.searchtx) {
         if (uzbl.state.verbose)
             printf ("Searching: %s\n", uzbl.state.searchtx);
@@ -801,24 +1107,32 @@ search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
 }
 
 static void
-search_forward_text (WebKitWebView *page, GArray *argv) {
+search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
+    (void) result;
     search_text(page, argv, TRUE);
 }
 
 static void
-search_reverse_text (WebKitWebView *page, GArray *argv) {
+search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
+    (void) result;
     search_text(page, argv, FALSE);
 }
 
 static void
-dehilight (WebKitWebView *page, GArray *argv) {
-    (void) argv;
+dehilight (WebKitWebView *page, GArray *argv, GString *result) {
+    (void) argv; (void) result;
     webkit_web_view_set_highlight_text_matches (page, FALSE);
 }
 
 
 static void
 new_window_load_uri (const gchar * uri) {
+    if (uzbl.behave.new_window) {
+        GString *s = g_string_new ("");
+        g_string_printf(s, "'%s'", uri);
+        run_handler(uzbl.behave.new_window, s->str);
+        return;
+    }
     GString* to_execute = g_string_new ("");
     g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
     int i;
@@ -837,48 +1151,56 @@ new_window_load_uri (const gchar * uri) {
 }
 
 static void
-chain (WebKitWebView *page, GArray *argv) {
-    (void)page;
+chain (WebKitWebView *page, GArray *argv, GString *result) {
+    (void) page; (void) result;
     gchar *a = NULL;
     gchar **parts = NULL;
-    guint i = 0;    
+    guint i = 0;
     while ((a = argv_idx(argv, i++))) {
         parts = g_strsplit (a, " ", 2);
-        parse_command(parts[0], parts[1]);
+        parse_command(parts[0], parts[1], result);
         g_strfreev (parts);
     }
 }
 
 static void
-keycmd (WebKitWebView *page, GArray *argv) {
+keycmd (WebKitWebView *page, GArray *argv, GString *result) {
     (void)page;
     (void)argv;
-    g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
+    (void)result;
+    uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
     run_keycmd(FALSE);
     update_title();
 }
 
 static void
-keycmd_nl (WebKitWebView *page, GArray *argv) {
+keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
     (void)page;
     (void)argv;
-    g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
+    (void)result;
+    uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
     run_keycmd(TRUE);
     update_title();
 }
 
 static void
-keycmd_bs (WebKitWebView *page, GArray *argv) {
+keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
+    gchar *prev;
     (void)page;
     (void)argv;
-    g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
+    (void)result;
+    int len = strlen(uzbl.state.keycmd);
+    prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
+    if (prev)
+      uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
     update_title();
 }
 
 static void
-close_uzbl (WebKitWebView *page, GArray *argv) {
+close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
     (void)page;
     (void)argv;
+    (void)result;
     gtk_main_quit ();
 }
 
@@ -901,208 +1223,6 @@ build_progressbar_ascii(int percent) {
 
    return g_string_free(bar, FALSE);
 }
-
-static void
-setup_scanner() {
-     const GScannerConfig scan_config = {
-             (
-              "\t\r\n"
-             )            /* cset_skip_characters */,
-             (
-              G_CSET_a_2_z
-              "_#"
-              G_CSET_A_2_Z
-             )            /* cset_identifier_first */,
-             (
-              G_CSET_a_2_z
-              "_0123456789"
-              G_CSET_A_2_Z
-              G_CSET_LATINS
-              G_CSET_LATINC
-             )            /* cset_identifier_nth */,
-             ( "" )    /* cpair_comment_single */,
-
-             TRUE         /* case_sensitive */,
-
-             FALSE        /* skip_comment_multi */,
-             FALSE        /* skip_comment_single */,
-             FALSE        /* scan_comment_multi */,
-             TRUE         /* scan_identifier */,
-             TRUE         /* scan_identifier_1char */,
-             FALSE        /* scan_identifier_NULL */,
-             TRUE         /* scan_symbols */,
-             FALSE        /* scan_binary */,
-             FALSE        /* scan_octal */,
-             FALSE        /* scan_float */,
-             FALSE        /* scan_hex */,
-             FALSE        /* scan_hex_dollar */,
-             FALSE        /* scan_string_sq */,
-             FALSE        /* scan_string_dq */,
-             TRUE         /* numbers_2_int */,
-             FALSE        /* int_2_float */,
-             FALSE        /* identifier_2_string */,
-             FALSE        /* char_2_token */,
-             FALSE        /* symbol_2_token */,
-             TRUE         /* scope_0_fallback */,
-             FALSE,
-             TRUE
-     };
-
-     uzbl.scan = g_scanner_new(&scan_config);
-     while(symp->symbol_name) {
-         g_scanner_scope_add_symbol(uzbl.scan, 0,
-                         symp->symbol_name,
-                         GINT_TO_POINTER(symp->symbol_token));
-         symp++;
-     }
-}
-
-static gchar *
-expand_template(const char *template, gboolean escape_markup) {
-     if(!template) return NULL;
-
-     GTokenType token = G_TOKEN_NONE;
-     GString *ret = g_string_new("");
-     char *buf=NULL;
-     int sym;
-
-     g_scanner_input_text(uzbl.scan, template, strlen(template));
-     while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
-         token = g_scanner_get_next_token(uzbl.scan);
-
-         if(token == G_TOKEN_SYMBOL) {
-             sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
-             switch(sym) {
-                 case SYM_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);
-                     g_free(buf);
-                     break;
-                 case SYM_LOADPRGSBAR:
-                     buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
-                     g_string_append(ret, buf);
-                     g_free(buf);
-                     break;
-                 case SYM_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:
-                     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);
-                     g_free(buf);
-                     break;
-                 case SYM_KEYCMD:
-                     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?
-                             uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
-                     break;
-                 case SYM_MSG:
-                     g_string_append(ret,
-                             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);
-                     g_free(buf);
-                     break;
-                 case SYM_WK_MIN:
-                     buf = itos(WEBKIT_MINOR_VERSION);
-                     g_string_append(ret, buf);
-                     g_free(buf);
-                     break;
-                 case SYM_WK_MIC:
-                     buf = itos(WEBKIT_MICRO_VERSION);
-                     g_string_append(ret, buf);
-                     g_free(buf);
-                     break;
-                 case SYM_SYSNAME:
-                     g_string_append(ret, uzbl.state.unameinfo.sysname);
-                     break;
-                 case SYM_NODENAME:
-                     g_string_append(ret, uzbl.state.unameinfo.nodename);
-                     break;
-                 case SYM_KERNREL:
-                     g_string_append(ret, uzbl.state.unameinfo.release);
-                     break;
-                 case SYM_KERNVER:
-                     g_string_append(ret, uzbl.state.unameinfo.version);
-                     break;
-                 case SYM_ARCHSYS:
-                     g_string_append(ret, uzbl.state.unameinfo.machine);
-                     break;
-                 case SYM_ARCHUZBL:
-                     g_string_append(ret, ARCH);
-                     break;
-#ifdef _GNU_SOURCE
-                 case SYM_DOMAINNAME:
-                     g_string_append(ret, uzbl.state.unameinfo.domainname);
-                     break;
-#endif
-                 case SYM_COMMIT:
-                     g_string_append(ret, COMMIT);
-                     break;
-                 default:
-                     break;
-             }
-         }
-         else if(token == G_TOKEN_INT) {
-             buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
-             g_string_append(ret, buf);
-             g_free(buf);
-         }
-         else if(token == G_TOKEN_IDENTIFIER) {
-             g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
-         }
-         else if(token == G_TOKEN_CHAR) {
-             g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
-         }
-     }
-
-     return g_string_free(ret, FALSE);
-}
 /* --End Statusbar functions-- */
 
 static void
@@ -1114,10 +1234,10 @@ 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;
-    
+
     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
     gchar *pid = itos(getpid());
     gchar *xwin = itos(uzbl.xwin);
@@ -1135,13 +1255,13 @@ 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) {
-        if (*stdout) *stdout = strfree(*stdout);
-        
+        if (*output_stdout) *output_stdout = strfree(*output_stdout);
+
         result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
-                              NULL, NULL, stdout, NULL, NULL, &err);
+                              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);
 
@@ -1155,8 +1275,8 @@ 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(stdout) {
-            printf("Stdout: %s\n", *stdout);
+        if(output_stdout) {
+            printf("Stdout: %s\n", *output_stdout);
         }
     }
     if (err) {
@@ -1174,7 +1294,7 @@ split_quoted(const gchar* src, const gboolean unquote) {
     /* split on unquoted space, return array of strings;
        remove a layer of quotes and backslashes if unquote */
     if (!src) return NULL;
-    
+
     gboolean dq = FALSE;
     gboolean sq = FALSE;
     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
@@ -1207,30 +1327,30 @@ split_quoted(const gchar* src, const gboolean unquote) {
 }
 
 static void
-spawn(WebKitWebView *web_view, GArray *argv) {
-    (void)web_view;
+spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
+    (void)web_view; (void)result;
     //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);
 }
 
 static void
-spawn_sync(WebKitWebView *web_view, GArray *argv) {
-    (void)web_view;
-    
+spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
+    (void)web_view; (void)result;
+
     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;
+spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
+    (void)web_view; (void)result;
     if (!uzbl.behave.shell_cmd) {
         g_printerr ("spawn_sh: shell_cmd is not set!\n");
         return;
     }
-    
+
     guint i;
     gchar *spacer = g_strdup("");
     g_array_insert_val(argv, 1, spacer);
@@ -1245,13 +1365,13 @@ spawn_sh(WebKitWebView *web_view, GArray *argv) {
 }
 
 static void
-spawn_sh_sync(WebKitWebView *web_view, GArray *argv) {
-    (void)web_view;
+spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
+    (void)web_view; (void)result;
     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);
@@ -1259,30 +1379,40 @@ spawn_sh_sync(WebKitWebView *web_view, GArray *argv) {
 
     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;
+void
+parse_command(const char *cmd, const char *param, GString *result) {
+    CommandInfo *c;
 
     if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
-
             guint i;
             gchar **par = split_quoted(param, TRUE);
             GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
 
-            if (c[1] == NOSPLIT) { /* don't split */
+            if (c->no_split) { /* don't split */
                 sharg_append(a, param);
             } else if (par) {
                 for (i = 0; i < g_strv_length(par); i++)
                     sharg_append(a, par[i]);
             }
-            c[0](uzbl.gui.web_view, a);
+
+            if (result == NULL) {
+                GString *result_print = g_string_new("");
+
+                c->function(uzbl.gui.web_view, a, result_print);
+                if (result_print->len)
+                    printf("%*s\n", result_print->len, result_print->str);
+
+                g_string_free(result_print, TRUE);
+            } else {
+                c->function(uzbl.gui.web_view, a, result);
+            }
             g_strfreev (par);
             g_array_free (a, TRUE);
 
@@ -1310,17 +1440,26 @@ set_proxy_url() {
 }
 
 static void
+set_icon() {
+    if(file_exists(uzbl.gui.icon)) {
+        if (uzbl.gui.main_window)
+            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);
+    }
+}
+
+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);
+    load_uri(uzbl.gui.web_view, a, NULL);
     g_array_free (a, TRUE);
 }
 
-static void 
+static void
 cmd_always_insert_mode() {
-    uzbl.behave.insert_mode =
-        uzbl.behave.always_insert_mode ?  TRUE : FALSE;
+    set_insert_mode(uzbl.behave.always_insert_mode);
     update_title();
 }
 
@@ -1359,7 +1498,7 @@ cmd_font_size() {
     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);
@@ -1376,7 +1515,7 @@ cmd_zoom_level() {
 
 static void
 cmd_disable_plugins() {
-    g_object_set (G_OBJECT(view_settings()), "enable-plugins", 
+    g_object_set (G_OBJECT(view_settings()), "enable-plugins",
             !uzbl.behave.disable_plugins, NULL);
 }
 
@@ -1423,31 +1562,31 @@ cmd_print_bg() {
             uzbl.behave.print_bg, NULL);
 }
 
-static void 
+static void
 cmd_style_uri() {
     g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
             uzbl.behave.style_uri, NULL);
 }
 
-static void 
+static void
 cmd_resizable_txt() {
     g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
             uzbl.behave.resizable_txt, NULL);
 }
 
-static void 
+static void
 cmd_default_encoding() {
     g_object_set (G_OBJECT(view_settings()), "default-encoding",
             uzbl.behave.default_encoding, NULL);
 }
 
-static void 
+static void
 cmd_enforce_96dpi() {
     g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
             uzbl.behave.enforce_96dpi, NULL);
 }
 
-static void 
+static void
 cmd_caret_browsing() {
     g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
             uzbl.behave.caret_browsing, NULL);
@@ -1467,6 +1606,19 @@ cmd_cookie_handler() {
 }
 
 static void
+cmd_new_window() {
+    gchar **split = g_strsplit(uzbl.behave.new_window, " ", 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.new_window);
+        uzbl.behave.new_window =
+            g_strdup_printf("%s %s", split[0], split[1]);
+    }
+    g_strfreev (split);
+}
+
+static void
 cmd_fifo_dir() {
     uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
 }
@@ -1492,7 +1644,7 @@ cmd_modkey() {
     buf = g_utf8_strup(uzbl.behave.modkey, -1);
     uzbl.behave.modmask = 0;
 
-    if(uzbl.behave.modkey) 
+    if(uzbl.behave.modkey)
         g_free(uzbl.behave.modkey);
     uzbl.behave.modkey = buf;
 
@@ -1502,17 +1654,14 @@ cmd_modkey() {
     }
 }
 
-static void
+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;
+        g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
+            uzbl.net.useragent, NULL);
     }
 }
 
@@ -1537,27 +1686,29 @@ move_statusbar() {
     return;
 }
 
-static gboolean
+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)) ) {
+        if(!c->writeable) return TRUE;
+
         /* check for the variable type */
         if (c->type == TYPE_STR) {
-            buf = expand_vars(val);
+            buf = expand(val, 0);
             g_free(*c->ptr);
             *c->ptr = buf;
         } else if(c->type == TYPE_INT) {
             int *ip = (int *)c->ptr;
-            buf = expand_vars(val);
+            buf = expand(val, 0);
             *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 = strtof(buf, &endp);
+            buf = expand(val, 0);
+            *fp = strtod(buf, &endp);
             g_free(buf);
         }
 
@@ -1581,7 +1732,7 @@ render_html() {
 
 enum {M_CMD, M_HTML};
 static void
-parse_cmd_line(const char *ctl_line) {
+parse_cmd_line(const char *ctl_line, GString *result) {
     Behaviour *b = &uzbl.behave;
     size_t len=0;
 
@@ -1614,7 +1765,7 @@ parse_cmd_line(const char *ctl_line) {
         else ctlstrip = g_strdup(ctl_line);
 
         tokens = g_strsplit(ctlstrip, " ", 2);
-        parse_command(tokens[0], tokens[1]);
+        parse_command(tokens[0], tokens[1], result);
         g_free(ctlstrip);
         g_strfreev(tokens);
     }
@@ -1622,21 +1773,16 @@ parse_cmd_line(const char *ctl_line) {
 
 static gchar*
 build_stream_name(int type, const gchar* dir) {
-    char *xwin_str;
     State *s = &uzbl.state;
-    gchar *str;
+    gchar *str = NULL;
 
-    xwin_str = itos((int)uzbl.xwin);
     if (type == FIFO) {
         str = g_strdup_printf
-            ("%s/uzbl_fifo_%s", dir,
-             s->instance_name ? s->instance_name : xwin_str);
+            ("%s/uzbl_fifo_%s", dir, s->instance_name);
     } else if (type == SOCKET) {
         str = g_strdup_printf
-            ("%s/uzbl_socket_%s", dir,
-             s->instance_name ? s->instance_name : xwin_str );
+            ("%s/uzbl_socket_%s", dir, s->instance_name);
     }
-    g_free(xwin_str);
     return str;
 }
 
@@ -1660,7 +1806,7 @@ control_fifo(GIOChannel *gio, GIOCondition condition) {
         g_error_free (err);
     }
 
-    parse_cmd_line(ctl_line);
+    parse_cmd_line(ctl_line, NULL);
     g_free(ctl_line);
 
     return TRUE;
@@ -1716,7 +1862,7 @@ control_stdin(GIOChannel *gio, GIOCondition condition) {
     if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
         return FALSE;
 
-    parse_cmd_line(ctl_line);
+    parse_cmd_line(ctl_line, NULL);
     g_free(ctl_line);
 
     return TRUE;
@@ -1744,53 +1890,53 @@ create_stdin () {
 static gboolean
 control_socket(GIOChannel *chan) {
     struct sockaddr_un remote;
-    char buffer[512], *ctl_line;
-    char temp[128];
-    int sock, clientsock, n, done;
-    unsigned int t;
-
-    sock = g_io_channel_unix_get_fd(chan);
-
-    memset (buffer, 0, sizeof (buffer));
+    unsigned int t = sizeof(remote);
+    int clientsock;
+    GIOChannel *clientchan;
 
-    t          = sizeof (remote);
-    clientsock = accept (sock, (struct sockaddr *) &remote, &t);
+    clientsock = accept (g_io_channel_unix_get_fd(chan),
+                         (struct sockaddr *) &remote, &t);
 
-    done = 0;
-    do {
-        memset (temp, 0, sizeof (temp));
-        n = recv (clientsock, temp, 128, 0);
-        if (n == 0) {
-            buffer[strlen (buffer)] = '\0';
-            done = 1;
-        }
-        if (!done)
-            strcat (buffer, temp);
-    } while (!done);
-
-    if (strcmp (buffer, "\n") < 0) {
-        buffer[strlen (buffer) - 1] = '\0';
-    } else {
-        buffer[strlen (buffer)] = '\0';
+    if ((clientchan = g_io_channel_unix_new(clientsock))) {
+        g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
+                       (GIOFunc) control_client_socket, clientchan);
     }
-    close (clientsock);
-    ctl_line = g_strdup(buffer);
-    parse_cmd_line (ctl_line);
 
-/*
-   TODO: we should be able to do it with this.  but glib errors out with "Invalid argument"
+    return TRUE;
+}
+
+static gboolean
+control_client_socket(GIOChannel *clientchan) {
+    char *ctl_line;
+    GString *result = g_string_new("");
     GError *error = NULL;
-    gsize len;
     GIOStatus ret;
-    ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
-    if (ret == G_IO_STATUS_ERROR)
-        g_error ("Error reading: %s\n", error->message);
+    gsize len;
 
-    printf("Got line %s (%u bytes) \n",ctl_line, len);
-    if(ctl_line) {
-       parse_line(ctl_line);
-*/
+    ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
+    if (ret == G_IO_STATUS_ERROR) {
+        g_warning ("Error reading: %s\n", error->message);
+        g_io_channel_shutdown(clientchan, TRUE, &error);
+        return FALSE;
+    } else if (ret == G_IO_STATUS_EOF) {
+        /* shutdown and remove channel watch from main loop */
+        g_io_channel_shutdown(clientchan, TRUE, &error);
+        return FALSE;
+    }
+
+    if (ctl_line) {
+        parse_cmd_line (ctl_line, result);
+        g_string_append_c(result, '\n');
+        ret = g_io_channel_write_chars (clientchan, result->str, result->len,
+                                        &len, &error);
+        if (ret == G_IO_STATUS_ERROR) {
+            g_warning ("Error writing: %s", error->message);
+        }
+        g_io_channel_flush(clientchan, &error);
+    }
 
+    if (error) g_error_free (error);
+    g_string_free(result, TRUE);
     g_free(ctl_line);
     return TRUE;
 }
@@ -1851,55 +1997,80 @@ update_title (void) {
 
     if (b->show_status) {
         if (b->title_format_short) {
-            parsed = expand_template(b->title_format_short, FALSE);
-            gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
+            parsed = expand(b->title_format_short, 0);
+            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, TRUE);
+            parsed = expand(b->status_format, 0);
             gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
             g_free(parsed);
         }
         if (b->status_background) {
             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);
+            //labels and hboxes do not draw their own background. applying this on the vbox/main_window is ok as the statusbar is the only affected widget. (if not, we could also use GtkEventBox)
+            if (uzbl.gui.main_window)
+                gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
+            else if (uzbl.gui.plug)
+                gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
         }
     } else {
         if (b->title_format_long) {
-            parsed = expand_template(b->title_format_long, FALSE);
-            gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
+            parsed = expand(b->title_format_long, 0);
+            if (uzbl.gui.main_window)
+                gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
             g_free(parsed);
         }
     }
 }
 
 static gboolean
+configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
+    (void) window;
+    (void) event;
+
+    retreive_geometry();
+    return FALSE;
+}
+
+static gboolean
 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) 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)
+    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)
         return FALSE;
 
     /* turn off insert mode (if always_insert_mode is not used) */
     if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
-        uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
+        set_insert_mode(uzbl.behave.always_insert_mode);
         update_title();
         return TRUE;
     }
 
-    if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
+    if (uzbl.behave.insert_mode && 
+        ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || 
+          (!uzbl.behave.modmask)
+        )
+       )
         return FALSE;
 
     if (event->keyval == GDK_Escape) {
-        g_string_truncate(uzbl.state.keycmd, 0);
+        clear_keycmd();
         update_title();
-        dehilight(uzbl.gui.web_view, NULL);
+        dehilight(uzbl.gui.web_view, NULL, NULL);
         return TRUE;
     }
 
@@ -1912,7 +2083,9 @@ key_press_cb (GtkWidget* window, GdkEventKey* event)
             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
         }
         if (str) {
-            g_string_append (uzbl.state.keycmd, str);
+            GString* keycmd = g_string_new(uzbl.state.keycmd);
+            g_string_append (keycmd, str);
+            uzbl.state.keycmd = g_string_free(keycmd, FALSE);
             update_title ();
             g_free (str);
         }
@@ -1920,12 +2093,16 @@ key_press_cb (GtkWidget* window, GdkEventKey* event)
     }
 
     if (event->keyval == GDK_BackSpace)
-        keycmd_bs(NULL, NULL);
+        keycmd_bs(NULL, NULL, NULL);
 
     gboolean key_ret = FALSE;
     if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
         key_ret = TRUE;
-    if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
+    if (!key_ret) {
+        GString* keycmd = g_string_new(uzbl.state.keycmd);
+        g_string_append(keycmd, event->string);
+        uzbl.state.keycmd = g_string_free(keycmd, FALSE);
+    }
 
     run_keycmd(key_ret);
     update_title();
@@ -1937,9 +2114,9 @@ static void
 run_keycmd(const gboolean key_ret) {
     /* run the keycmd immediately if it isn't incremental and doesn't take args */
     Action *act;
-    if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
-        g_string_truncate(uzbl.state.keycmd, 0);
-        parse_command(act->name, act->param);
+    if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
+        clear_keycmd();
+        parse_command(act->name, act->param, NULL);
         return;
     }
 
@@ -1947,8 +2124,9 @@ run_keycmd(const gboolean key_ret) {
     GString* short_keys = g_string_new ("");
     GString* short_keys_inc = g_string_new ("");
     guint i;
-    for (i=0; i<(uzbl.state.keycmd->len); i++) {
-        g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
+    guint len = strlen(uzbl.state.keycmd);
+    for (i=0; i<len; i++) {
+        g_string_append_c(short_keys, uzbl.state.keycmd[i]);
         g_string_assign(short_keys_inc, short_keys->str);
         g_string_append_c(short_keys, '_');
         g_string_append_c(short_keys_inc, '*');
@@ -1956,15 +2134,15 @@ run_keycmd(const gboolean 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);
+            clear_keycmd();
             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);
+                clear_keycmd();
             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);
@@ -1973,7 +2151,7 @@ run_keycmd(const gboolean key_ret) {
 
 static void
 exec_paramcmd(const Action *act, const guint i) {
-    GString *parampart = g_string_new (uzbl.state.keycmd->str);
+    GString *parampart = g_string_new (uzbl.state.keycmd);
     GString *actionname = g_string_new ("");
     GString *actionparam = g_string_new ("");
     g_string_erase (parampart, 0, i+1);
@@ -1981,14 +2159,14 @@ exec_paramcmd(const Action *act, const guint i) {
         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);
+    parse_command(actionname->str, actionparam->str, NULL);
     g_string_free(actionname, TRUE);
     g_string_free(actionparam, TRUE);
     g_string_free(parampart, TRUE);
 }
 
 
-static GtkWidget*
+GtkWidget*
 create_browser () {
     GUI *g = &uzbl.gui;
 
@@ -1999,7 +2177,7 @@ create_browser () {
     g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
     gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
 
-    g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
+    g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
     g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
     g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
     g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
@@ -2029,6 +2207,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;
 }
 
@@ -2039,10 +2218,21 @@ GtkWidget* create_window () {
     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);
+    g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
 
     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) {
     /*
@@ -2106,7 +2296,7 @@ run_handler (const gchar *act, const gchar *args) {
     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;
@@ -2114,7 +2304,7 @@ run_handler (const gchar *act, const gchar *args) {
         gchar *quotless = NULL;
         gchar **spliced_quotless = NULL; // sigh -_-;
         gchar **inpart = NULL;
-        
+
         while (*cp) {
             if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
                 quot = **cp;
@@ -2124,27 +2314,27 @@ run_handler (const gchar *act, const gchar *args) {
             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]));
+        parse_command(parts[0], &(newargs->str[1]), NULL);
         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]);
+        parse_command(inparts[0], inparts[1], NULL);
         g_free(inparts[0]);
         g_free(inparts[1]);
     }
     g_strfreev(parts);
 }
 
-static void
+void
 add_binding (const gchar *key, const gchar *act) {
     char **parts = g_strsplit(act, " ", 2);
     Action *action;
@@ -2167,8 +2357,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) {
@@ -2176,7 +2365,10 @@ get_xdg_var (XDG_Var xdg) {
         } else {
             return_value = NULL;
         }
+    } else {
+        return_value = str_replace("~", home, actual_value);
     }
+
     return return_value;
 }
 
@@ -2204,7 +2396,7 @@ find_xdg_file (int xdg_type, char* filename) {
             temporary_file = g_strconcat (temporary_string, filename, NULL);
         }
     }
-    
+
     //g_free (temporary_string); - segfaults.
 
     if (file_exists (temporary_file)) {
@@ -2219,10 +2411,15 @@ settings_init () {
     Network *n = &uzbl.net;
     int i;
     for (i = 0; default_config[i].command != NULL; i++) {
-        parse_cmd_line(default_config[i].command);
+        parse_cmd_line(default_config[i].command, NULL);
+    }
+    
+    if (g_strcmp0(s->config_file, "-") == 0) {
+        s->config_file = NULL;
+        create_stdin();
     }
 
-    if (!s->config_file) {
+    else if (!s->config_file) {
         s->config_file = find_xdg_file (0, "/uzbl/config");
     }
 
@@ -2232,7 +2429,7 @@ settings_init () {
         gchar* line;
 
         while ((line = g_array_index(lines, gchar*, i))) {
-            parse_cmd_line (line);
+            parse_cmd_line (line, NULL);
             i ++;
             g_free (line);
         }
@@ -2264,7 +2461,7 @@ static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer use
     }
     if (uzbl.comm.sync_stdout)
         uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
-        
+
     g_string_free(s, TRUE);
 }
 
@@ -2343,7 +2540,7 @@ inspector_attach_window_cb (WebKitWebInspector* inspector){
 }
 
 static gboolean
-inspector_dettach_window_cb (WebKitWebInspector* inspector){
+inspector_detach_window_cb (WebKitWebInspector* inspector){
     (void) inspector;
     return FALSE;
 }
@@ -2371,8 +2568,8 @@ set_up_inspector() {
     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), "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);
 }
@@ -2401,14 +2598,30 @@ dump_key_hash(gpointer k, gpointer v, gpointer ud) {
 }
 
 static void
-dump_config() {
+dump_config() { //ADD "result" var so we can use this with uzblctrl
     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[]) {
+static void
+retreive_geometry() {
+    int w, h, x, y;
+    GString *buf = g_string_new("");
+
+    gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
+    gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
+
+    g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
+
+    if(uzbl.gui.geometry)
+        g_free(uzbl.gui.geometry);
+    uzbl.gui.geometry = g_string_free(buf, FALSE);
+}
+
+/* set up gtk, gobject, variable defaults and other things that tests and other
+ * external applications need to do anyhow */
+void
+initialize(int argc, char *argv[]) {
     gtk_init (&argc, &argv);
     if (!g_thread_supported ())
         g_thread_init (NULL);
@@ -2416,20 +2629,22 @@ main (int argc, char* argv[]) {
     uzbl.state.selected_url = NULL;
     uzbl.state.searchtx = NULL;
 
-    GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
+    GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
     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);
-    
-    gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
-    gboolean verbose_override = uzbl.state.verbose;
+
+    if (uzbl.behave.print_version) {
+        printf("Commit: %s\n", COMMIT);
+        exit(0);
+    }
 
     /* initialize hash table */
     uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
 
     uzbl.net.soup_session = webkit_get_default_session();
-    uzbl.state.keycmd = g_string_new("");
+    uzbl.state.keycmd = g_strdup("");
 
     if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
         fprintf(stderr, "uzbl: error hooking SIGTERM\n");
@@ -2438,10 +2653,6 @@ main (int argc, char* argv[]) {
     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;
@@ -2456,30 +2667,54 @@ main (int argc, char* argv[]) {
     uzbl.behave.insert_indicator = g_strdup("I");
     uzbl.behave.cmd_indicator    = g_strdup("C");
 
-    setup_scanner();
+    uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
+    uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
+    uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
+    uzbl.info.arch         = ARCH;
+    uzbl.info.commit       = COMMIT;
+
     commands_hash ();
     make_var_to_name_hash();
 
+    uzbl.gui.scrolled_win = create_browser();
+}
+
+#ifndef UZBL_LIBRARY
+/** -- MAIN -- **/
+int
+main (int argc, char* argv[]) {
+    initialize(argc, argv);
+
     uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
 
-    uzbl.gui.scrolled_win = create_browser();
     create_mainbar();
 
     /* initial packing */
     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);
+    }
 
+    if(!uzbl.state.instance_name)
+        uzbl.state.instance_name = itos((int)uzbl.xwin);
 
     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);
     }
@@ -2490,7 +2725,16 @@ main (int argc, char* argv[]) {
     uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
     gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
 
+    if(uzbl.gui.geometry)
+        cmd_set_geometry();
+    else
+        retreive_geometry();
+
+    gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
+    gboolean verbose_override = uzbl.state.verbose;
+
     settings_init ();
+    set_insert_mode(FALSE);
 
     if (!uzbl.behave.show_status)
         gtk_widget_hide(uzbl.gui.mainbar);
@@ -2500,11 +2744,9 @@ main (int argc, char* argv[]) {
     /* WebInspector */
     set_up_inspector();
 
-    create_stdin();
-
     if (verbose_override > uzbl.state.verbose)
         uzbl.state.verbose = verbose_override;
-    
+
     if (uri_override) {
         set_var_value("uri", uri_override);
         g_free(uri_override);
@@ -2516,5 +2758,6 @@ main (int argc, char* argv[]) {
 
     return EXIT_SUCCESS;
 }
+#endif
 
 /* vi: set et ts=4: */