Added a GtkPlug which plugs into a socket ID specified by -s.
[uzbl-mobile] / uzbl.c
1 /* -*- c-basic-offset: 4; -*- */
2 // Original code taken from the example webkit-gtk+ application. see notice below.
3 // Modified code is licensed under the GPL 3.  See LICENSE file.
4
5
6 /*
7  * Copyright (C) 2006, 2007 Apple Inc.
8  * Copyright (C) 2007 Alp Toker <alp@atoker.com>
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
20  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32
33 #define LENGTH(x) (sizeof x / sizeof x[0])
34 #define MAX_BINDINGS 256
35 #define _POSIX_SOURCE
36
37 #include <gtk/gtk.h>
38 #include <gdk/gdkx.h>
39 #include <gdk/gdkkeysyms.h>
40 #include <sys/socket.h>
41 #include <sys/stat.h>
42 #include <sys/types.h>
43 #include <sys/un.h>
44 #include <sys/utsname.h>
45 #include <sys/time.h>
46 #include <webkit/webkit.h>
47 #include <libsoup/soup.h>
48
49 #include <stdio.h>
50 #include <string.h>
51 #include <unistd.h>
52 #include <stdlib.h>
53 #include <errno.h>
54 #include <fcntl.h>
55 #include <signal.h>
56 #include "uzbl.h"
57 #include "config.h"
58
59 static Uzbl uzbl;
60 typedef void (*Command)(WebKitWebView*, GArray *argv);
61
62
63
64 /* commandline arguments (set initial values for the state variables) */
65 static const 
66 GOptionEntry entries[] =
67 {
68     { "uri",     'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
69         "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
70     { "verbose", 'v', 0, G_OPTION_ARG_NONE,   &uzbl.state.verbose,
71         "Whether to print all messages or just errors.", NULL },
72     { "name",    'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name, 
73         "Name of the current instance (defaults to Xorg window id)", "NAME" },
74     { "config",  'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,   
75         "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
76     { "socket",  's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,   
77         "Socket ID", "SOCKET" },
78     { NULL,      0, 0, 0, NULL, NULL, NULL }
79 };
80
81 /* associate command names to their properties */
82 typedef const struct {
83     void **ptr;
84     int type;
85     int dump;
86     void (*func)(void);
87 } uzbl_cmdprop;
88
89 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
90
91 /* an abbreviation to help keep the table's width humane */
92 #define PTR(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .func = fun }
93
94 const struct {
95     char *name;
96     uzbl_cmdprop cp;
97 } var_name_to_ptr[] = {
98 /*    variable name         pointer to variable in code          type  dump callback function    */
99 /*  --------------------------------------------------------------------------------------- */
100     { "uri",                 PTR(uzbl.state.uri,                  STR,  1,   cmd_load_uri)},
101     { "verbose",             PTR(uzbl.state.verbose,              INT,  1,   NULL)},
102     { "mode",                PTR(uzbl.behave.mode,                INT,  0,   NULL)},
103     { "inject_html",         PTR(uzbl.behave.inject_html,         STR,  0,   cmd_inject_html)},
104     { "base_url",            PTR(uzbl.behave.base_url,            STR,  1,   NULL)},
105     { "html_endmarker",      PTR(uzbl.behave.html_endmarker,      STR,  1,   NULL)},
106     { "html_mode_timeout",   PTR(uzbl.behave.html_timeout,        INT,  1,   NULL)},
107     { "status_message",      PTR(uzbl.gui.sbar.msg,               STR,  1,   update_title)},
108     { "show_status",         PTR(uzbl.behave.show_status,         INT,  1,   cmd_set_status)},
109     { "status_top",          PTR(uzbl.behave.status_top,          INT,  1,   move_statusbar)},
110     { "status_format",       PTR(uzbl.behave.status_format,       STR,  1,   update_title)},
111     { "status_pbar_done",    PTR(uzbl.gui.sbar.progress_s,        STR,  1,   update_title)},
112     { "status_pbar_pending", PTR(uzbl.gui.sbar.progress_u,        STR,  1,   update_title)},
113     { "status_pbar_width",   PTR(uzbl.gui.sbar.progress_w,        INT,  1,   update_title)},
114     { "status_background",   PTR(uzbl.behave.status_background,   STR,  1,   update_title)},
115     { "insert_indicator",    PTR(uzbl.behave.insert_indicator,    STR,  1,   update_title)},
116     { "command_indicator",   PTR(uzbl.behave.cmd_indicator,       STR,  1,   update_title)},
117     { "title_format_long",   PTR(uzbl.behave.title_format_long,   STR,  1,   update_title)},
118     { "title_format_short",  PTR(uzbl.behave.title_format_short,  STR,  1,   update_title)},
119     { "insert_mode",         PTR(uzbl.behave.insert_mode,         INT,  1,   NULL)},
120     { "always_insert_mode",  PTR(uzbl.behave.always_insert_mode,  INT,  1,   cmd_always_insert_mode)},
121     { "reset_command_mode",  PTR(uzbl.behave.reset_command_mode,  INT,  1,   NULL)},
122     { "modkey",              PTR(uzbl.behave.modkey,              STR,  1,   cmd_modkey)},
123     { "load_finish_handler", PTR(uzbl.behave.load_finish_handler, STR,  1,   NULL)},
124     { "load_start_handler",  PTR(uzbl.behave.load_start_handler,  STR,  1,   NULL)},
125     { "load_commit_handler", PTR(uzbl.behave.load_commit_handler, STR,  1,   NULL)},
126     { "history_handler",     PTR(uzbl.behave.history_handler,     STR,  1,   NULL)},
127     { "download_handler",    PTR(uzbl.behave.download_handler,    STR,  1,   NULL)},
128     { "cookie_handler",      PTR(uzbl.behave.cookie_handler,      STR,  1,   cmd_cookie_handler)},
129     { "fifo_dir",            PTR(uzbl.behave.fifo_dir,            STR,  1,   cmd_fifo_dir)},
130     { "socket_dir",          PTR(uzbl.behave.socket_dir,          STR,  1,   cmd_socket_dir)},
131     { "http_debug",          PTR(uzbl.behave.http_debug,          INT,  1,   cmd_http_debug)},
132     { "shell_cmd",           PTR(uzbl.behave.shell_cmd,           STR,  1,   NULL)},
133     { "proxy_url",           PTR(uzbl.net.proxy_url,              STR,  1,   set_proxy_url)},
134     { "max_conns",           PTR(uzbl.net.max_conns,              INT,  1,   cmd_max_conns)},
135     { "max_conns_host",      PTR(uzbl.net.max_conns_host,         INT,  1,   cmd_max_conns_host)},
136     { "useragent",           PTR(uzbl.net.useragent,              STR,  1,   cmd_useragent)},
137     /* exported WebKitWebSettings properties */
138     { "zoom_level",          PTR(uzbl.behave.zoom_level,          FLOAT,1,   cmd_zoom_level)},
139     { "font_size",           PTR(uzbl.behave.font_size,           INT,  1,   cmd_font_size)},
140     { "monospace_size",      PTR(uzbl.behave.monospace_size,      INT,  1,   cmd_font_size)},
141     { "minimum_font_size",   PTR(uzbl.behave.minimum_font_size,   INT,  1,   cmd_minimum_font_size)},
142     { "disable_plugins",     PTR(uzbl.behave.disable_plugins,     INT,  1,   cmd_disable_plugins)},
143     { "disable_scripts",     PTR(uzbl.behave.disable_scripts,     INT,  1,   cmd_disable_scripts)},
144     { "autoload_images",     PTR(uzbl.behave.autoload_img,        INT,  1,   cmd_autoload_img)},
145     { "autoshrink_images",   PTR(uzbl.behave.autoshrink_img,      INT,  1,   cmd_autoshrink_img)},
146     { "enable_spellcheck",   PTR(uzbl.behave.enable_spellcheck,   INT,  1,   cmd_enable_spellcheck)},
147     { "enable_private",      PTR(uzbl.behave.enable_private,      INT,  1,   cmd_enable_private)},
148     { "print_backgrounds",   PTR(uzbl.behave.print_bg,            INT,  1,   cmd_print_bg)},
149     { "stylesheet_uri",      PTR(uzbl.behave.style_uri,           STR,  1,   cmd_style_uri)},
150     { "resizable_text_areas",PTR(uzbl.behave.resizable_txt,       INT,  1,   cmd_resizable_txt)},
151     { "default_encoding",    PTR(uzbl.behave.default_encoding,    STR,  1,   cmd_default_encoding)},
152     { "enforce_96_dpi",      PTR(uzbl.behave.enforce_96dpi,       INT,  1,   cmd_enforce_96dpi)},
153     { "caret_browsing",      PTR(uzbl.behave.caret_browsing,      INT,  1,   cmd_caret_browsing)},
154
155     { NULL,                  {.ptr = NULL, .type = TYPE_INT, .dump = 0, .func = NULL}}
156 }, *n2v_p = var_name_to_ptr;
157
158 const struct {
159     char *key;
160     guint mask;
161 } modkeys[] = {
162     { "SHIFT",   GDK_SHIFT_MASK   }, // shift
163     { "LOCK",    GDK_LOCK_MASK    }, // capslock or shiftlock, depending on xserver's modmappings
164     { "CONTROL", GDK_CONTROL_MASK }, // control
165     { "MOD1",    GDK_MOD1_MASK    }, // 4th mod - normally alt but depends on modmappings
166     { "MOD2",    GDK_MOD2_MASK    }, // 5th mod
167     { "MOD3",    GDK_MOD3_MASK    }, // 6th mod
168     { "MOD4",    GDK_MOD4_MASK    }, // 7th mod
169     { "MOD5",    GDK_MOD5_MASK    }, // 8th mod
170     { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
171     { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
172     { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
173     { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
174     { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
175     { "SUPER",   GDK_SUPER_MASK   }, // super (since 2.10)
176     { "HYPER",   GDK_HYPER_MASK   }, // hyper (since 2.10)
177     { "META",    GDK_META_MASK    }, // meta (since 2.10)
178     { NULL,      0                }
179 };
180
181
182 /* construct a hash from the var_name_to_ptr array for quick access */
183 static void
184 make_var_to_name_hash() {
185     uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
186     while(n2v_p->name) {
187         g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
188         n2v_p++;
189     }
190 }
191
192 /* --- UTILITY FUNCTIONS --- */
193 static gchar *
194 expand_vars(char *s) {
195     uzbl_cmdprop *c;
196     char upto = ' ';
197     char ret[256],  *vend;
198     GString *buf = g_string_new("");
199
200     while(*s) {
201         switch(*s) {
202             case '\\':
203                 g_string_append_c(buf, *++s);
204                 s++;
205                 break;
206             case '@':
207                 if(*(s+1) == '{') {
208                     upto = '}'; s++;
209                 }
210                 s++;
211                 if( (vend = strchr(s, upto)) ||
212                         (vend = strchr(s, '\0')) ) {
213                     strncpy(ret, s, vend-s);
214                     ret[vend-s] = '\0';
215                     if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
216                         if(c->type == TYPE_STR)
217                             g_string_append(buf, (gchar *)*c->ptr);
218                         else if(c->type == TYPE_INT) {
219                             char *b = itos((int)*c->ptr);
220                             g_string_append(buf, b);
221                             g_free(b);
222                         }
223                     }
224                     if(upto == ' ') s = vend;
225                     else s = vend+1;
226                     upto = ' ';
227                 }
228                 break;
229             default:
230                 g_string_append_c(buf, *s);
231                 s++;
232                 break;
233         }
234     }
235     return g_string_free(buf, FALSE);
236 }
237
238 char *
239 itos(int val) {
240     char tmp[20];
241
242     snprintf(tmp, sizeof(tmp), "%i", val);
243     return g_strdup(tmp);
244 }
245
246 static gchar*
247 strfree(gchar *str) { g_free(str); return NULL; }  // for freeing & setting to null in one go
248
249 static gchar*
250 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
251
252 static char *
253 str_replace (const char* search, const char* replace, const char* string) {
254     gchar **buf;
255     char *ret;
256
257     buf = g_strsplit (string, search, -1);
258     ret = g_strjoinv (replace, buf);
259     g_strfreev(buf); // somebody said this segfaults
260
261     return ret;
262 }
263
264 static GArray*
265 read_file_by_line (gchar *path) {
266     GIOChannel *chan = NULL;
267     gchar *readbuf = NULL;
268     gsize len;
269     GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
270     int i = 0;
271     
272     chan = g_io_channel_new_file(path, "r", NULL);
273     
274     if (chan) {
275         while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
276             const gchar* val = g_strdup (readbuf);
277             g_array_append_val (lines, val);
278             g_free (readbuf);
279             i ++;
280         }
281         
282         g_io_channel_unref (chan);
283     } else {
284         fprintf(stderr, "File '%s' not be read.\n", path);
285     }
286     
287     return lines;
288 }
289
290 static
291 gchar* parseenv (char* string) {
292     extern char** environ;
293     gchar* tmpstr = NULL;
294     int i = 0;
295     
296
297     while (environ[i] != NULL) {
298         gchar** env = g_strsplit (environ[i], "=", 2);
299         gchar* envname = g_strconcat ("$", env[0], NULL);
300
301         if (g_strrstr (string, envname) != NULL) {
302             tmpstr = g_strdup(string);
303             g_free (string);
304             string = str_replace(envname, env[1], tmpstr);
305             g_free (tmpstr);
306         }
307
308         g_free (envname);
309         g_strfreev (env); // somebody said this breaks uzbl
310         i++;
311     }
312
313     return string;
314 }
315
316 static sigfunc*
317 setup_signal(int signr, sigfunc *shandler) {
318     struct sigaction nh, oh;
319
320     nh.sa_handler = shandler;
321     sigemptyset(&nh.sa_mask);
322     nh.sa_flags = 0;
323
324     if(sigaction(signr, &nh, &oh) < 0)
325         return SIG_ERR;
326
327     return NULL;
328 }
329
330 static void
331 clean_up(void) {
332     if (uzbl.behave.fifo_dir)
333         unlink (uzbl.comm.fifo_path);
334     if (uzbl.behave.socket_dir)
335         unlink (uzbl.comm.socket_path);
336
337     g_free(uzbl.state.executable_path);
338     g_string_free(uzbl.state.keycmd, TRUE);
339     g_hash_table_destroy(uzbl.bindings);
340     g_hash_table_destroy(uzbl.behave.commands);
341 }
342
343 /* used for html_mode_timeout 
344  * be sure to extend this function to use
345  * more timers if needed in other places
346 */
347 static void
348 set_timeout(int seconds) {
349     struct itimerval t;
350     memset(&t, 0, sizeof t);
351
352     t.it_value.tv_sec =  seconds;
353     t.it_value.tv_usec = 0;
354     setitimer(ITIMER_REAL, &t, NULL);
355 }
356
357 /* --- SIGNAL HANDLER --- */
358
359 static void
360 catch_sigterm(int s) {
361     (void) s;
362     clean_up();
363 }
364
365 static void
366 catch_sigint(int s) {
367     (void) s;
368     clean_up();
369     exit(EXIT_SUCCESS);
370 }
371
372 static void
373 catch_alrm(int s) {
374     (void) s;
375
376     set_var_value("mode", "0");
377     render_html();
378 }
379
380
381 /* --- CALLBACKS --- */
382
383 static gboolean
384 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
385     (void) web_view;
386     (void) frame;
387     (void) navigation_action;
388     (void) policy_decision;
389     (void) user_data;
390     const gchar* uri = webkit_network_request_get_uri (request);
391     if (uzbl.state.verbose)
392         printf("New window requested -> %s \n", uri);
393     new_window_load_uri(uri);
394     return (FALSE);
395 }
396
397 static gboolean
398 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type,  WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
399     (void) frame;
400     (void) request;
401     (void) user_data;
402
403     /* If we can display it, let's display it... */
404     if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
405         webkit_web_policy_decision_use (policy_decision);
406         return TRUE;
407     }
408
409     /* ...everything we can't displayed is downloaded */
410     webkit_web_policy_decision_download (policy_decision);
411     return TRUE;
412 }
413
414 WebKitWebView*
415 create_web_view_cb (WebKitWebView  *web_view, WebKitWebFrame *frame, gpointer user_data) {
416     (void) web_view;
417     (void) frame;
418     (void) user_data;
419     if (uzbl.state.selected_url != NULL) {
420         if (uzbl.state.verbose)
421             printf("\nNew web view -> %s\n",uzbl.state.selected_url);
422         new_window_load_uri(uzbl.state.selected_url);
423     } else {
424         if (uzbl.state.verbose)
425             printf("New web view -> %s\n","Nothing to open, exiting");
426     }
427     return (NULL);
428 }
429
430 static gboolean
431 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
432     (void) web_view;
433     (void) user_data;
434     if (uzbl.behave.download_handler) {
435         const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
436         if (uzbl.state.verbose)
437             printf("Download -> %s\n",uri);
438         /* if urls not escaped, we may have to escape and quote uri before this call */
439         run_handler(uzbl.behave.download_handler, uri);
440     }
441     return (FALSE);
442 }
443
444 /* scroll a bar in a given direction */
445 static void
446 scroll (GtkAdjustment* bar, GArray *argv) {
447     gdouble amount;
448     gchar *end;
449
450     amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
451     if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
452     gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
453 }
454
455 static void
456 scroll_begin(WebKitWebView* page, GArray *argv) {
457     (void) page; (void) argv;
458     gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
459 }
460
461 static void
462 scroll_end(WebKitWebView* page, GArray *argv) {
463     (void) page; (void) argv;
464     gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
465                               gtk_adjustment_get_page_size(uzbl.gui.bar_v));
466 }
467
468 static void
469 scroll_vert(WebKitWebView* page, GArray *argv) {
470     (void) page;
471     scroll(uzbl.gui.bar_v, argv);
472 }
473
474 static void
475 scroll_horz(WebKitWebView* page, GArray *argv) {
476     (void) page;
477     scroll(uzbl.gui.bar_h, argv);
478 }
479
480 static void
481 cmd_set_status() {
482     if (!uzbl.behave.show_status) {
483         gtk_widget_hide(uzbl.gui.mainbar);
484     } else {
485         gtk_widget_show(uzbl.gui.mainbar);
486     }
487     update_title();
488 }
489
490 static void
491 toggle_status_cb (WebKitWebView* page, GArray *argv) {
492     (void)page;
493     (void)argv;
494
495     if (uzbl.behave.show_status) {
496         gtk_widget_hide(uzbl.gui.mainbar);
497     } else {
498         gtk_widget_show(uzbl.gui.mainbar);
499     }
500     uzbl.behave.show_status = !uzbl.behave.show_status;
501     update_title();
502 }
503
504 static void
505 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
506     (void) page;
507     (void) title;
508     (void) data;
509     //Set selected_url state variable
510     g_free(uzbl.state.selected_url);
511     uzbl.state.selected_url = NULL;
512     if (link) {
513         uzbl.state.selected_url = g_strdup(link);
514     }
515     update_title();
516 }
517
518 static void
519 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
520     (void) web_view;
521     (void) web_frame;
522     (void) data;
523     if (uzbl.gui.main_title)
524         g_free (uzbl.gui.main_title);
525     uzbl.gui.main_title = g_strdup (title);
526     update_title();
527 }
528
529 static void
530 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
531     (void) page;
532     (void) data;
533     uzbl.gui.sbar.load_progress = progress;
534     update_title();
535 }
536
537 static void
538 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
539     (void) page;
540     (void) frame;
541     (void) data;
542     if (uzbl.behave.load_finish_handler)
543         run_handler(uzbl.behave.load_finish_handler, "");
544 }
545
546 static void
547 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
548     (void) page;
549     (void) frame;
550     (void) data;
551     uzbl.gui.sbar.load_progress = 0;
552     g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
553     if (uzbl.behave.load_start_handler)
554         run_handler(uzbl.behave.load_start_handler, "");
555 }
556
557 static void
558 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
559     (void) page;
560     (void) data;
561     g_free (uzbl.state.uri);
562     GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
563     uzbl.state.uri = g_string_free (newuri, FALSE);
564     if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
565         uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
566         update_title();
567     }
568     if (uzbl.behave.load_commit_handler)
569         run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
570 }
571
572 static void
573 destroy_cb (GtkWidget* widget, gpointer data) {
574     (void) widget;
575     (void) data;
576     gtk_main_quit ();
577 }
578
579 static void
580 log_history_cb () {
581    if (uzbl.behave.history_handler) {
582        time_t rawtime;
583        struct tm * timeinfo;
584        char date [80];
585        time ( &rawtime );
586        timeinfo = localtime ( &rawtime );
587        strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
588        run_handler(uzbl.behave.history_handler, date);
589    }
590 }
591
592
593 /* VIEW funcs (little webkit wrappers) */
594 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv){(void)argv; webkit_web_view_##name(page);}
595 VIEWFUNC(reload)
596 VIEWFUNC(reload_bypass_cache)
597 VIEWFUNC(stop_loading)
598 VIEWFUNC(zoom_in)
599 VIEWFUNC(zoom_out)
600 VIEWFUNC(go_back)
601 VIEWFUNC(go_forward)
602 #undef VIEWFUNC
603
604 /* -- command to callback/function map for things we cannot attach to any signals */
605 static struct {char *name; Command command[2];} cmdlist[] =
606 {   /* key                   function      no_split      */
607     { "back",               {view_go_back, 0}              },
608     { "forward",            {view_go_forward, 0}           },
609     { "scroll_vert",        {scroll_vert, 0}               },
610     { "scroll_horz",        {scroll_horz, 0}               },
611     { "scroll_begin",       {scroll_begin, 0}              },
612     { "scroll_end",         {scroll_end, 0}                },
613     { "reload",             {view_reload, 0},              },
614     { "reload_ign_cache",   {view_reload_bypass_cache, 0}  },
615     { "stop",               {view_stop_loading, 0},        },
616     { "zoom_in",            {view_zoom_in, 0},             }, //Can crash (when max zoom reached?).
617     { "zoom_out",           {view_zoom_out, 0},            },
618     { "uri",                {load_uri, NOSPLIT}            },
619     { "js",                 {run_js, NOSPLIT}              },
620     { "script",             {run_external_js, 0}           },
621     { "toggle_status",      {toggle_status_cb, 0}          },
622     { "spawn",              {spawn, 0}                     },
623     { "sync_spawn",         {spawn_sync, 0}                }, // needed for cookie handler
624     { "sh",                 {spawn_sh, 0}                  },
625     { "sync_sh",            {spawn_sh_sync, 0}             }, // needed for cookie handler
626     { "exit",               {close_uzbl, 0}                },
627     { "search",             {search_forward_text, NOSPLIT} },
628     { "search_reverse",     {search_reverse_text, NOSPLIT} },
629     { "dehilight",          {dehilight, 0}                 },
630     { "toggle_insert_mode", {toggle_insert_mode, 0}        },
631     { "set",                {set_var, NOSPLIT}             },
632     //{ "get",                {get_var, NOSPLIT}             },
633     { "bind",               {act_bind, NOSPLIT}            },
634     { "dump_config",        {act_dump_config, 0}           },
635     { "keycmd",             {keycmd, NOSPLIT}              },
636     { "keycmd_nl",          {keycmd_nl, NOSPLIT}           },
637     { "keycmd_bs",          {keycmd_bs, 0}                 },
638     { "chain",              {chain, 0}                     },
639     { "print",              {print, NOSPLIT}               }
640 };
641
642 static void
643 commands_hash(void)
644 {
645     unsigned int i;
646     uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
647
648     for (i = 0; i < LENGTH(cmdlist); i++)
649         g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
650 }
651
652 /* -- CORE FUNCTIONS -- */
653
654 void
655 free_action(gpointer act) {
656     Action *action = (Action*)act;
657     g_free(action->name);
658     if (action->param)
659         g_free(action->param);
660     g_free(action);
661 }
662
663 Action*
664 new_action(const gchar *name, const gchar *param) {
665     Action *action = g_new(Action, 1);
666
667     action->name = g_strdup(name);
668     if (param)
669         action->param = g_strdup(param);
670     else
671         action->param = NULL;
672
673     return action;
674 }
675
676 static bool
677 file_exists (const char * filename) {
678     return (access(filename, F_OK) == 0);
679 }
680
681 static void
682 set_var(WebKitWebView *page, GArray *argv) {
683     (void) page;
684     gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
685     gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
686     set_var_value(g_strstrip(split[0]), value);
687     g_free(value);
688     g_strfreev(split);
689 }
690
691 static void
692 print(WebKitWebView *page, GArray *argv) {
693     (void) page;
694     gchar* buf;
695
696     buf = expand_vars(argv_idx(argv, 0));
697     puts(buf);
698     g_free(buf);
699 }
700
701 static void
702 act_bind(WebKitWebView *page, GArray *argv) {
703     (void) page;
704     gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
705     gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
706     add_binding(g_strstrip(split[0]), value);
707     g_free(value);
708     g_strfreev(split);
709 }
710
711
712 static void
713 act_dump_config() {
714     dump_config();
715 }
716
717 static void
718 toggle_insert_mode(WebKitWebView *page, GArray *argv) {
719     (void)page;
720
721     if (argv_idx(argv, 0)) {
722         if (strcmp (argv_idx(argv, 0), "0") == 0) {
723             uzbl.behave.insert_mode = FALSE;
724         } else {
725             uzbl.behave.insert_mode = TRUE;
726         }
727     } else {
728         uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
729     }
730
731     update_title();
732 }
733
734 static void
735 load_uri (WebKitWebView *web_view, GArray *argv) {
736     if (argv_idx(argv, 0)) {
737         GString* newuri = g_string_new (argv_idx(argv, 0));
738         if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
739             run_js(web_view, argv);
740             return;
741         }
742         if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
743             g_string_prepend (newuri, "http://");
744         /* if we do handle cookies, ask our handler for them */
745         webkit_web_view_load_uri (web_view, newuri->str);
746         g_string_free (newuri, TRUE);
747     }
748 }
749
750 static void
751 run_js (WebKitWebView * web_view, GArray *argv) {
752     if (argv_idx(argv, 0))
753         webkit_web_view_execute_script (web_view, argv_idx(argv, 0));
754 }
755
756 static void
757 run_external_js (WebKitWebView * web_view, GArray *argv) {
758     if (argv_idx(argv, 0)) {
759         GArray* lines = read_file_by_line (argv_idx (argv, 0));
760         gchar*  js = NULL;
761         int i = 0;
762         gchar* line;
763
764         while ((line = g_array_index(lines, gchar*, i))) {
765             if (js == NULL) {
766                 js = g_strdup (line);
767             } else {
768                 gchar* newjs = g_strconcat (js, line, NULL);
769                 js = newjs;
770             }
771             i ++;
772             g_free (line);
773         }
774         
775         if (uzbl.state.verbose)
776             printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
777
778         if (argv_idx (argv, 1)) {
779             gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
780             g_free (js);
781             js = newjs;
782         }
783         webkit_web_view_execute_script (web_view, js);
784         g_free (js);
785         g_array_free (lines, TRUE);
786     }
787 }
788
789 static void
790 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
791     if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
792         if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
793             webkit_web_view_unmark_text_matches (page);
794             webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
795             uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
796         }
797     }
798     
799     if (uzbl.state.searchtx) {
800         if (uzbl.state.verbose)
801             printf ("Searching: %s\n", uzbl.state.searchtx);
802         webkit_web_view_set_highlight_text_matches (page, TRUE);
803         webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
804     }
805 }
806
807 static void
808 search_forward_text (WebKitWebView *page, GArray *argv) {
809     search_text(page, argv, TRUE);
810 }
811
812 static void
813 search_reverse_text (WebKitWebView *page, GArray *argv) {
814     search_text(page, argv, FALSE);
815 }
816
817 static void
818 dehilight (WebKitWebView *page, GArray *argv) {
819     (void) argv;
820     webkit_web_view_set_highlight_text_matches (page, FALSE);
821 }
822
823
824 static void
825 new_window_load_uri (const gchar * uri) {
826     GString* to_execute = g_string_new ("");
827     g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
828     int i;
829     for (i = 0; entries[i].long_name != NULL; i++) {
830         if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
831             gchar** str = (gchar**)entries[i].arg_data;
832             if (*str!=NULL) {
833                 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
834             }
835         }
836     }
837     if (uzbl.state.verbose)
838         printf("\n%s\n", to_execute->str);
839     g_spawn_command_line_async (to_execute->str, NULL);
840     g_string_free (to_execute, TRUE);
841 }
842
843 static void
844 chain (WebKitWebView *page, GArray *argv) {
845     (void)page;
846     gchar *a = NULL;
847     gchar **parts = NULL;
848     guint i = 0;    
849     while ((a = argv_idx(argv, i++))) {
850         parts = g_strsplit (a, " ", 2);
851         parse_command(parts[0], parts[1]);
852         g_strfreev (parts);
853     }
854 }
855
856 static void
857 keycmd (WebKitWebView *page, GArray *argv) {
858     (void)page;
859     (void)argv;
860     g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
861     run_keycmd(FALSE);
862     update_title();
863 }
864
865 static void
866 keycmd_nl (WebKitWebView *page, GArray *argv) {
867     (void)page;
868     (void)argv;
869     g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
870     run_keycmd(TRUE);
871     update_title();
872 }
873
874 static void
875 keycmd_bs (WebKitWebView *page, GArray *argv) {
876     (void)page;
877     (void)argv;
878     g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
879     update_title();
880 }
881
882 static void
883 close_uzbl (WebKitWebView *page, GArray *argv) {
884     (void)page;
885     (void)argv;
886     gtk_main_quit ();
887 }
888
889 /* --Statusbar functions-- */
890 static char*
891 build_progressbar_ascii(int percent) {
892    int width=uzbl.gui.sbar.progress_w;
893    int i;
894    double l;
895    GString *bar = g_string_new("");
896
897    l = (double)percent*((double)width/100.);
898    l = (int)(l+.5)>=(int)l ? l+.5 : l;
899
900    for(i=0; i<(int)l; i++)
901        g_string_append(bar, uzbl.gui.sbar.progress_s);
902
903    for(; i<width; i++)
904        g_string_append(bar, uzbl.gui.sbar.progress_u);
905
906    return g_string_free(bar, FALSE);
907 }
908
909 static void
910 setup_scanner() {
911      const GScannerConfig scan_config = {
912              (
913               "\t\r\n"
914              )            /* cset_skip_characters */,
915              (
916               G_CSET_a_2_z
917               "_#"
918               G_CSET_A_2_Z
919              )            /* cset_identifier_first */,
920              (
921               G_CSET_a_2_z
922               "_0123456789"
923               G_CSET_A_2_Z
924               G_CSET_LATINS
925               G_CSET_LATINC
926              )            /* cset_identifier_nth */,
927              ( "" )    /* cpair_comment_single */,
928
929              TRUE         /* case_sensitive */,
930
931              FALSE        /* skip_comment_multi */,
932              FALSE        /* skip_comment_single */,
933              FALSE        /* scan_comment_multi */,
934              TRUE         /* scan_identifier */,
935              TRUE         /* scan_identifier_1char */,
936              FALSE        /* scan_identifier_NULL */,
937              TRUE         /* scan_symbols */,
938              FALSE        /* scan_binary */,
939              FALSE        /* scan_octal */,
940              FALSE        /* scan_float */,
941              FALSE        /* scan_hex */,
942              FALSE        /* scan_hex_dollar */,
943              FALSE        /* scan_string_sq */,
944              FALSE        /* scan_string_dq */,
945              TRUE         /* numbers_2_int */,
946              FALSE        /* int_2_float */,
947              FALSE        /* identifier_2_string */,
948              FALSE        /* char_2_token */,
949              FALSE        /* symbol_2_token */,
950              TRUE         /* scope_0_fallback */,
951              FALSE,
952              TRUE
953      };
954
955      uzbl.scan = g_scanner_new(&scan_config);
956      while(symp->symbol_name) {
957          g_scanner_scope_add_symbol(uzbl.scan, 0,
958                          symp->symbol_name,
959                          GINT_TO_POINTER(symp->symbol_token));
960          symp++;
961      }
962 }
963
964 static gchar *
965 expand_template(const char *template, gboolean escape_markup) {
966      if(!template) return NULL;
967
968      GTokenType token = G_TOKEN_NONE;
969      GString *ret = g_string_new("");
970      char *buf=NULL;
971      int sym;
972
973      g_scanner_input_text(uzbl.scan, template, strlen(template));
974      while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
975          token = g_scanner_get_next_token(uzbl.scan);
976
977          if(token == G_TOKEN_SYMBOL) {
978              sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
979              switch(sym) {
980                  case SYM_URI:
981                      if(escape_markup) {
982                          buf = uzbl.state.uri?
983                              g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
984                          g_string_append(ret, buf);
985                          g_free(buf);
986                      }
987                      else
988                          g_string_append(ret, uzbl.state.uri?
989                                  uzbl.state.uri:g_strdup(""));
990                      break;
991                  case SYM_LOADPRGS:
992                      buf = itos(uzbl.gui.sbar.load_progress);
993                      g_string_append(ret, buf);
994                      g_free(buf);
995                      break;
996                  case SYM_LOADPRGSBAR:
997                      buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
998                      g_string_append(ret, buf);
999                      g_free(buf);
1000                      break;
1001                  case SYM_TITLE:
1002                      if(escape_markup) {
1003                          buf = uzbl.gui.main_title?
1004                              g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
1005                          g_string_append(ret, buf);
1006                          g_free(buf);
1007                      }
1008                      else
1009                          g_string_append(ret, uzbl.gui.main_title?
1010                                  uzbl.gui.main_title:g_strdup(""));
1011                      break;
1012                  case SYM_SELECTED_URI:
1013                      if(escape_markup) {
1014                          buf = uzbl.state.selected_url?
1015                              g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
1016                          g_string_append(ret, buf);
1017                          g_free(buf);
1018                      }
1019                      else
1020                          g_string_append(ret, uzbl.state.selected_url?
1021                                  uzbl.state.selected_url:g_strdup(""));
1022                      break;
1023                  case SYM_NAME:
1024                      buf = itos(uzbl.xwin);
1025                      g_string_append(ret,
1026                              uzbl.state.instance_name?uzbl.state.instance_name:buf);
1027                      g_free(buf);
1028                      break;
1029                  case SYM_KEYCMD:
1030                      if(escape_markup) {
1031                          buf = uzbl.state.keycmd->str?
1032                              g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1033                          g_string_append(ret, buf);
1034                          g_free(buf);
1035                      }
1036                      else
1037                          g_string_append(ret, uzbl.state.keycmd->str?
1038                                  uzbl.state.keycmd->str:g_strdup(""));
1039                      break;
1040                  case SYM_MODE:
1041                      g_string_append(ret,
1042                              uzbl.behave.insert_mode?
1043                              uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1044                      break;
1045                  case SYM_MSG:
1046                      g_string_append(ret,
1047                              uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1048                      break;
1049                      /* useragent syms */
1050                  case SYM_WK_MAJ:
1051                      buf = itos(WEBKIT_MAJOR_VERSION);
1052                      g_string_append(ret, buf);
1053                      g_free(buf);
1054                      break;
1055                  case SYM_WK_MIN:
1056                      buf = itos(WEBKIT_MINOR_VERSION);
1057                      g_string_append(ret, buf);
1058                      g_free(buf);
1059                      break;
1060                  case SYM_WK_MIC:
1061                      buf = itos(WEBKIT_MICRO_VERSION);
1062                      g_string_append(ret, buf);
1063                      g_free(buf);
1064                      break;
1065                  case SYM_SYSNAME:
1066                      g_string_append(ret, uzbl.state.unameinfo.sysname);
1067                      break;
1068                  case SYM_NODENAME:
1069                      g_string_append(ret, uzbl.state.unameinfo.nodename);
1070                      break;
1071                  case SYM_KERNREL:
1072                      g_string_append(ret, uzbl.state.unameinfo.release);
1073                      break;
1074                  case SYM_KERNVER:
1075                      g_string_append(ret, uzbl.state.unameinfo.version);
1076                      break;
1077                  case SYM_ARCHSYS:
1078                      g_string_append(ret, uzbl.state.unameinfo.machine);
1079                      break;
1080                  case SYM_ARCHUZBL:
1081                      g_string_append(ret, ARCH);
1082                      break;
1083 #ifdef _GNU_SOURCE
1084                  case SYM_DOMAINNAME:
1085                      g_string_append(ret, uzbl.state.unameinfo.domainname);
1086                      break;
1087 #endif
1088                  case SYM_COMMIT:
1089                      g_string_append(ret, COMMIT);
1090                      break;
1091                  default:
1092                      break;
1093              }
1094          }
1095          else if(token == G_TOKEN_INT) {
1096              buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1097              g_string_append(ret, buf);
1098              g_free(buf);
1099          }
1100          else if(token == G_TOKEN_IDENTIFIER) {
1101              g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1102          }
1103          else if(token == G_TOKEN_CHAR) {
1104              g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1105          }
1106      }
1107
1108      return g_string_free(ret, FALSE);
1109 }
1110 /* --End Statusbar functions-- */
1111
1112 static void
1113 sharg_append(GArray *a, const gchar *str) {
1114     const gchar *s = (str ? str : "");
1115     g_array_append_val(a, s);
1116 }
1117
1118 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1119 static gboolean
1120 run_command (const gchar *command, const guint npre, const gchar **args,
1121              const gboolean sync, char **output_stdout) {
1122    //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1123     GError *err = NULL;
1124     
1125     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1126     gchar *pid = itos(getpid());
1127     gchar *xwin = itos(uzbl.xwin);
1128     guint i;
1129     sharg_append(a, command);
1130     for (i = 0; i < npre; i++) /* add n args before the default vars */
1131         sharg_append(a, args[i]);
1132     sharg_append(a, uzbl.state.config_file);
1133     sharg_append(a, pid);
1134     sharg_append(a, xwin);
1135     sharg_append(a, uzbl.comm.fifo_path);
1136     sharg_append(a, uzbl.comm.socket_path);
1137     sharg_append(a, uzbl.state.uri);
1138     sharg_append(a, uzbl.gui.main_title);
1139
1140     for (i = npre; i < g_strv_length((gchar**)args); i++)
1141         sharg_append(a, args[i]);
1142     
1143     gboolean result;
1144     if (sync) {
1145         if (*output_stdout) *output_stdout = strfree(*output_stdout);
1146         
1147         result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1148                               NULL, NULL, output_stdout, NULL, NULL, &err);
1149     } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1150                                   NULL, NULL, NULL, &err);
1151
1152     if (uzbl.state.verbose) {
1153         GString *s = g_string_new("spawned:");
1154         for (i = 0; i < (a->len); i++) {
1155             gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1156             g_string_append_printf(s, " %s", qarg);
1157             g_free (qarg);
1158         }
1159         g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1160         printf("%s\n", s->str);
1161         g_string_free(s, TRUE);
1162         if(output_stdout) {
1163             printf("Stdout: %s\n", *output_stdout);
1164         }
1165     }
1166     if (err) {
1167         g_printerr("error on run_command: %s\n", err->message);
1168         g_error_free (err);
1169     }
1170     g_free (pid);
1171     g_free (xwin);
1172     g_array_free (a, TRUE);
1173     return result;
1174 }
1175
1176 static gchar**
1177 split_quoted(const gchar* src, const gboolean unquote) {
1178     /* split on unquoted space, return array of strings;
1179        remove a layer of quotes and backslashes if unquote */
1180     if (!src) return NULL;
1181     
1182     gboolean dq = FALSE;
1183     gboolean sq = FALSE;
1184     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1185     GString *s = g_string_new ("");
1186     const gchar *p;
1187     gchar **ret;
1188     gchar *dup;
1189     for (p = src; *p != '\0'; p++) {
1190         if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1191         else if (*p == '\\') { g_string_append_c(s, *p++);
1192                                g_string_append_c(s, *p); }
1193         else if ((*p == '"') && unquote && !sq) dq = !dq;
1194         else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1195                                      dq = !dq; }
1196         else if ((*p == '\'') && unquote && !dq) sq = !sq;
1197         else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1198                                       sq = ! sq; }
1199         else if ((*p == ' ') && !dq && !sq) {
1200             dup = g_strdup(s->str);
1201             g_array_append_val(a, dup);
1202             g_string_truncate(s, 0);
1203         } else g_string_append_c(s, *p);
1204     }
1205     dup = g_strdup(s->str);
1206     g_array_append_val(a, dup);
1207     ret = (gchar**)a->data;
1208     g_array_free (a, FALSE);
1209     g_string_free (s, TRUE);
1210     return ret;
1211 }
1212
1213 static void
1214 spawn(WebKitWebView *web_view, GArray *argv) {
1215     (void)web_view;
1216     //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1217     if (argv_idx(argv, 0))
1218         run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1219 }
1220
1221 static void
1222 spawn_sync(WebKitWebView *web_view, GArray *argv) {
1223     (void)web_view;
1224     
1225     if (argv_idx(argv, 0))
1226         run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1227                     TRUE, &uzbl.comm.sync_stdout);
1228 }
1229
1230 static void
1231 spawn_sh(WebKitWebView *web_view, GArray *argv) {
1232     (void)web_view;
1233     if (!uzbl.behave.shell_cmd) {
1234         g_printerr ("spawn_sh: shell_cmd is not set!\n");
1235         return;
1236     }
1237     
1238     guint i;
1239     gchar *spacer = g_strdup("");
1240     g_array_insert_val(argv, 1, spacer);
1241     gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1242
1243     for (i = 1; i < g_strv_length(cmd); i++)
1244         g_array_prepend_val(argv, cmd[i]);
1245
1246     if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1247     g_free (spacer);
1248     g_strfreev (cmd);
1249 }
1250
1251 static void
1252 spawn_sh_sync(WebKitWebView *web_view, GArray *argv) {
1253     (void)web_view;
1254     if (!uzbl.behave.shell_cmd) {
1255         g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1256         return;
1257     }
1258     
1259     guint i;
1260     gchar *spacer = g_strdup("");
1261     g_array_insert_val(argv, 1, spacer);
1262     gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1263
1264     for (i = 1; i < g_strv_length(cmd); i++)
1265         g_array_prepend_val(argv, cmd[i]);
1266          
1267     if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1268                          TRUE, &uzbl.comm.sync_stdout);
1269     g_free (spacer);
1270     g_strfreev (cmd);
1271 }
1272
1273 static void
1274 parse_command(const char *cmd, const char *param) {
1275     Command *c;
1276
1277     if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1278
1279             guint i;
1280             gchar **par = split_quoted(param, TRUE);
1281             GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1282
1283             if (c[1] == NOSPLIT) { /* don't split */
1284                 sharg_append(a, param);
1285             } else if (par) {
1286                 for (i = 0; i < g_strv_length(par); i++)
1287                     sharg_append(a, par[i]);
1288             }
1289             c[0](uzbl.gui.web_view, a);
1290             g_strfreev (par);
1291             g_array_free (a, TRUE);
1292
1293     } else
1294         g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1295 }
1296
1297 static void
1298 set_proxy_url() {
1299     SoupURI *suri;
1300
1301     if(*uzbl.net.proxy_url == ' '
1302        || uzbl.net.proxy_url == NULL) {
1303         soup_session_remove_feature_by_type(uzbl.net.soup_session,
1304                 (GType) SOUP_SESSION_PROXY_URI);
1305     }
1306     else {
1307         suri = soup_uri_new(uzbl.net.proxy_url);
1308         g_object_set(G_OBJECT(uzbl.net.soup_session),
1309                 SOUP_SESSION_PROXY_URI,
1310                 suri, NULL);
1311         soup_uri_free(suri);
1312     }
1313     return;
1314 }
1315
1316 static void
1317 cmd_load_uri() {
1318     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1319     g_array_append_val (a, uzbl.state.uri);
1320     load_uri(uzbl.gui.web_view, a);
1321     g_array_free (a, TRUE);
1322 }
1323
1324 static void 
1325 cmd_always_insert_mode() {
1326     uzbl.behave.insert_mode =
1327         uzbl.behave.always_insert_mode ?  TRUE : FALSE;
1328     update_title();
1329 }
1330
1331 static void
1332 cmd_max_conns() {
1333     g_object_set(G_OBJECT(uzbl.net.soup_session),
1334             SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1335 }
1336
1337 static void
1338 cmd_max_conns_host() {
1339     g_object_set(G_OBJECT(uzbl.net.soup_session),
1340             SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1341 }
1342
1343 static void
1344 cmd_http_debug() {
1345     soup_session_remove_feature
1346         (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1347     /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1348     /*g_free(uzbl.net.soup_logger);*/
1349
1350     uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1351     soup_session_add_feature(uzbl.net.soup_session,
1352             SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1353 }
1354
1355 static WebKitWebSettings*
1356 view_settings() {
1357     return webkit_web_view_get_settings(uzbl.gui.web_view);
1358 }
1359
1360 static void
1361 cmd_font_size() {
1362     WebKitWebSettings *ws = view_settings();
1363     if (uzbl.behave.font_size > 0) {
1364         g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1365     }
1366     
1367     if (uzbl.behave.monospace_size > 0) {
1368         g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1369                       uzbl.behave.monospace_size, NULL);
1370     } else {
1371         g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1372                       uzbl.behave.font_size, NULL);
1373     }
1374 }
1375
1376 static void
1377 cmd_zoom_level() {
1378     webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1379 }
1380
1381 static void
1382 cmd_disable_plugins() {
1383     g_object_set (G_OBJECT(view_settings()), "enable-plugins", 
1384             !uzbl.behave.disable_plugins, NULL);
1385 }
1386
1387 static void
1388 cmd_disable_scripts() {
1389     g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1390             !uzbl.behave.disable_scripts, NULL);
1391 }
1392
1393 static void
1394 cmd_minimum_font_size() {
1395     g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1396             uzbl.behave.minimum_font_size, NULL);
1397 }
1398 static void
1399 cmd_autoload_img() {
1400     g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1401             uzbl.behave.autoload_img, NULL);
1402 }
1403
1404
1405 static void
1406 cmd_autoshrink_img() {
1407     g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1408             uzbl.behave.autoshrink_img, NULL);
1409 }
1410
1411
1412 static void
1413 cmd_enable_spellcheck() {
1414     g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1415             uzbl.behave.enable_spellcheck, NULL);
1416 }
1417
1418 static void
1419 cmd_enable_private() {
1420     g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1421             uzbl.behave.enable_private, NULL);
1422 }
1423
1424 static void
1425 cmd_print_bg() {
1426     g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1427             uzbl.behave.print_bg, NULL);
1428 }
1429
1430 static void 
1431 cmd_style_uri() {
1432     g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1433             uzbl.behave.style_uri, NULL);
1434 }
1435
1436 static void 
1437 cmd_resizable_txt() {
1438     g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1439             uzbl.behave.resizable_txt, NULL);
1440 }
1441
1442 static void 
1443 cmd_default_encoding() {
1444     g_object_set (G_OBJECT(view_settings()), "default-encoding",
1445             uzbl.behave.default_encoding, NULL);
1446 }
1447
1448 static void 
1449 cmd_enforce_96dpi() {
1450     g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1451             uzbl.behave.enforce_96dpi, NULL);
1452 }
1453
1454 static void 
1455 cmd_caret_browsing() {
1456     g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1457             uzbl.behave.caret_browsing, NULL);
1458 }
1459
1460 static void
1461 cmd_cookie_handler() {
1462     gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1463     /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1464     if ((g_strcmp0(split[0], "sh") == 0) ||
1465         (g_strcmp0(split[0], "spawn") == 0)) {
1466         g_free (uzbl.behave.cookie_handler);
1467         uzbl.behave.cookie_handler =
1468             g_strdup_printf("sync_%s %s", split[0], split[1]);
1469     }
1470     g_strfreev (split);
1471 }
1472
1473 static void
1474 cmd_fifo_dir() {
1475     uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1476 }
1477
1478 static void
1479 cmd_socket_dir() {
1480     uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1481 }
1482
1483 static void
1484 cmd_inject_html() {
1485     if(uzbl.behave.inject_html) {
1486         webkit_web_view_load_html_string (uzbl.gui.web_view,
1487                 uzbl.behave.inject_html, NULL);
1488     }
1489 }
1490
1491 static void
1492 cmd_modkey() {
1493     int i;
1494     char *buf;
1495
1496     buf = g_utf8_strup(uzbl.behave.modkey, -1);
1497     uzbl.behave.modmask = 0;
1498
1499     if(uzbl.behave.modkey) 
1500         g_free(uzbl.behave.modkey);
1501     uzbl.behave.modkey = buf;
1502
1503     for (i = 0; modkeys[i].key != NULL; i++) {
1504         if (g_strrstr(buf, modkeys[i].key))
1505             uzbl.behave.modmask |= modkeys[i].mask;
1506     }
1507 }
1508
1509 static void
1510 cmd_useragent() {
1511     if (*uzbl.net.useragent == ' ') {
1512         g_free (uzbl.net.useragent);
1513         uzbl.net.useragent = NULL;
1514     } else {
1515         gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1516         if (ua)
1517             g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1518         g_free(uzbl.net.useragent);
1519         uzbl.net.useragent = ua;
1520     }
1521 }
1522
1523 static void
1524 move_statusbar() {
1525     gtk_widget_ref(uzbl.gui.scrolled_win);
1526     gtk_widget_ref(uzbl.gui.mainbar);
1527     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1528     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1529
1530     if(uzbl.behave.status_top) {
1531         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1532         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1533     }
1534     else {
1535         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1536         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1537     }
1538     gtk_widget_unref(uzbl.gui.scrolled_win);
1539     gtk_widget_unref(uzbl.gui.mainbar);
1540     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1541     return;
1542 }
1543
1544 static gboolean
1545 set_var_value(gchar *name, gchar *val) {
1546     uzbl_cmdprop *c = NULL;
1547     char *endp = NULL;
1548     char *buf = NULL;
1549
1550     if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1551         /* check for the variable type */
1552         if (c->type == TYPE_STR) {
1553             buf = expand_vars(val);
1554             g_free(*c->ptr);
1555             *c->ptr = buf;
1556         } else if(c->type == TYPE_INT) {
1557             int *ip = (int *)c->ptr;
1558             buf = expand_vars(val);
1559             *ip = (int)strtoul(buf, &endp, 10);
1560             g_free(buf);
1561         } else if (c->type == TYPE_FLOAT) {
1562             float *fp = (float *)c->ptr;
1563             buf = expand_vars(val);
1564             *fp = strtod(buf, &endp);
1565             g_free(buf);
1566         }
1567
1568         /* invoke a command specific function */
1569         if(c->func) c->func();
1570     }
1571     return TRUE;
1572 }
1573
1574 static void
1575 render_html() {
1576     Behaviour *b = &uzbl.behave;
1577
1578     if(b->html_buffer->str) {
1579         webkit_web_view_load_html_string (uzbl.gui.web_view,
1580                 b->html_buffer->str, b->base_url);
1581         g_string_free(b->html_buffer, TRUE);
1582         b->html_buffer = g_string_new("");
1583     }
1584 }
1585
1586 enum {M_CMD, M_HTML};
1587 static void
1588 parse_cmd_line(const char *ctl_line) {
1589     Behaviour *b = &uzbl.behave;
1590     size_t len=0;
1591
1592     if(b->mode == M_HTML) {
1593         len = strlen(b->html_endmarker);
1594         /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1595         if(len == strlen(ctl_line)-1 &&
1596            !strncmp(b->html_endmarker, ctl_line, len)) {
1597             set_timeout(0);
1598             set_var_value("mode", "0");
1599             render_html();
1600             return;
1601         }
1602         else {
1603             set_timeout(b->html_timeout);
1604             g_string_append(b->html_buffer, ctl_line);
1605         }
1606     }
1607     else if((ctl_line[0] == '#') /* Comments */
1608             || (ctl_line[0] == ' ')
1609             || (ctl_line[0] == '\n'))
1610         ; /* ignore these lines */
1611     else { /* parse a command */
1612         gchar *ctlstrip;
1613         gchar **tokens = NULL;
1614         len = strlen(ctl_line);
1615
1616         if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1617             ctlstrip = g_strndup(ctl_line, len - 1);
1618         else ctlstrip = g_strdup(ctl_line);
1619
1620         tokens = g_strsplit(ctlstrip, " ", 2);
1621         parse_command(tokens[0], tokens[1]);
1622         g_free(ctlstrip);
1623         g_strfreev(tokens);
1624     }
1625 }
1626
1627 static gchar*
1628 build_stream_name(int type, const gchar* dir) {
1629     char *xwin_str;
1630     State *s = &uzbl.state;
1631     gchar *str;
1632
1633     xwin_str = itos((int)uzbl.xwin);
1634     if (type == FIFO) {
1635         str = g_strdup_printf
1636             ("%s/uzbl_fifo_%s", dir,
1637              s->instance_name ? s->instance_name : xwin_str);
1638     } else if (type == SOCKET) {
1639         str = g_strdup_printf
1640             ("%s/uzbl_socket_%s", dir,
1641              s->instance_name ? s->instance_name : xwin_str );
1642     }
1643     g_free(xwin_str);
1644     return str;
1645 }
1646
1647 static gboolean
1648 control_fifo(GIOChannel *gio, GIOCondition condition) {
1649     if (uzbl.state.verbose)
1650         printf("triggered\n");
1651     gchar *ctl_line;
1652     GIOStatus ret;
1653     GError *err = NULL;
1654
1655     if (condition & G_IO_HUP)
1656         g_error ("Fifo: Read end of pipe died!\n");
1657
1658     if(!gio)
1659        g_error ("Fifo: GIOChannel broke\n");
1660
1661     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1662     if (ret == G_IO_STATUS_ERROR) {
1663         g_error ("Fifo: Error reading: %s\n", err->message);
1664         g_error_free (err);
1665     }
1666
1667     parse_cmd_line(ctl_line);
1668     g_free(ctl_line);
1669
1670     return TRUE;
1671 }
1672
1673 static gchar*
1674 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1675     if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1676         if (unlink(uzbl.comm.fifo_path) == -1)
1677             g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1678         g_free(uzbl.comm.fifo_path);
1679         uzbl.comm.fifo_path = NULL;
1680     }
1681
1682     if (*dir == ' ') { /* space unsets the variable */
1683         g_free (dir);
1684         return NULL;
1685     }
1686
1687     GIOChannel *chan = NULL;
1688     GError *error = NULL;
1689     gchar *path = build_stream_name(FIFO, dir);
1690
1691     if (!file_exists(path)) {
1692         if (mkfifo (path, 0666) == 0) {
1693             // we don't really need to write to the file, but if we open the file as 'r' we will block here, waiting for a writer to open the file.
1694             chan = g_io_channel_new_file(path, "r+", &error);
1695             if (chan) {
1696                 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1697                     if (uzbl.state.verbose)
1698                         printf ("init_fifo: created successfully as %s\n", path);
1699                     uzbl.comm.fifo_path = path;
1700                     return dir;
1701                 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1702             } else g_warning ("init_fifo: can't open: %s\n", error->message);
1703         } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1704     } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1705
1706     /* if we got this far, there was an error; cleanup */
1707     if (error) g_error_free (error);
1708     g_free(dir);
1709     g_free(path);
1710     return NULL;
1711 }
1712
1713 static gboolean
1714 control_stdin(GIOChannel *gio, GIOCondition condition) {
1715     (void) condition;
1716     gchar *ctl_line = NULL;
1717     GIOStatus ret;
1718
1719     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1720     if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1721         return FALSE;
1722
1723     parse_cmd_line(ctl_line);
1724     g_free(ctl_line);
1725
1726     return TRUE;
1727 }
1728
1729 static void
1730 create_stdin () {
1731     GIOChannel *chan = NULL;
1732     GError *error = NULL;
1733
1734     chan = g_io_channel_unix_new(fileno(stdin));
1735     if (chan) {
1736         if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1737             g_error ("Stdin: could not add watch\n");
1738         } else {
1739             if (uzbl.state.verbose)
1740                 printf ("Stdin: watch added successfully\n");
1741         }
1742     } else {
1743         g_error ("Stdin: Error while opening: %s\n", error->message);
1744     }
1745     if (error) g_error_free (error);
1746 }
1747
1748 static gboolean
1749 control_socket(GIOChannel *chan) {
1750     struct sockaddr_un remote;
1751     char buffer[512], *ctl_line;
1752     char temp[128];
1753     int sock, clientsock, n, done;
1754     unsigned int t;
1755
1756     sock = g_io_channel_unix_get_fd(chan);
1757
1758     memset (buffer, 0, sizeof (buffer));
1759
1760     t          = sizeof (remote);
1761     clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1762
1763     done = 0;
1764     do {
1765         memset (temp, 0, sizeof (temp));
1766         n = recv (clientsock, temp, 128, 0);
1767         if (n == 0) {
1768             buffer[strlen (buffer)] = '\0';
1769             done = 1;
1770         }
1771         if (!done)
1772             strcat (buffer, temp);
1773     } while (!done);
1774
1775     if (strcmp (buffer, "\n") < 0) {
1776         buffer[strlen (buffer) - 1] = '\0';
1777     } else {
1778         buffer[strlen (buffer)] = '\0';
1779     }
1780     close (clientsock);
1781     ctl_line = g_strdup(buffer);
1782     parse_cmd_line (ctl_line);
1783
1784 /*
1785    TODO: we should be able to do it with this.  but glib errors out with "Invalid argument"
1786     GError *error = NULL;
1787     gsize len;
1788     GIOStatus ret;
1789     ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1790     if (ret == G_IO_STATUS_ERROR)
1791         g_error ("Error reading: %s\n", error->message);
1792
1793     printf("Got line %s (%u bytes) \n",ctl_line, len);
1794     if(ctl_line) {
1795        parse_line(ctl_line);
1796 */
1797
1798     g_free(ctl_line);
1799     return TRUE;
1800 }
1801
1802 static gchar*
1803 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1804     if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1805         if (unlink(uzbl.comm.socket_path) == -1)
1806             g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1807         g_free(uzbl.comm.socket_path);
1808         uzbl.comm.socket_path = NULL;
1809     }
1810
1811     if (*dir == ' ') {
1812         g_free(dir);
1813         return NULL;
1814     }
1815
1816     GIOChannel *chan = NULL;
1817     int sock, len;
1818     struct sockaddr_un local;
1819     gchar *path = build_stream_name(SOCKET, dir);
1820
1821     sock = socket (AF_UNIX, SOCK_STREAM, 0);
1822
1823     local.sun_family = AF_UNIX;
1824     strcpy (local.sun_path, path);
1825     unlink (local.sun_path);
1826
1827     len = strlen (local.sun_path) + sizeof (local.sun_family);
1828     if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1829         if (uzbl.state.verbose)
1830             printf ("init_socket: opened in %s\n", path);
1831         listen (sock, 5);
1832
1833         if( (chan = g_io_channel_unix_new(sock)) ) {
1834             g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1835             uzbl.comm.socket_path = path;
1836             return dir;
1837         }
1838     } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1839
1840     /* if we got this far, there was an error; cleanup */
1841     g_free(path);
1842     g_free(dir);
1843     return NULL;
1844 }
1845
1846 /*
1847  NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1848  it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1849 */
1850 // this function may be called very early when the templates are not set (yet), hence the checks
1851 static void
1852 update_title (void) {
1853     Behaviour *b = &uzbl.behave;
1854     gchar *parsed;
1855
1856     if (b->show_status) {
1857         if (b->title_format_short) {
1858             parsed = expand_template(b->title_format_short, FALSE);
1859             if (uzbl.gui.main_window)
1860                 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1861             g_free(parsed);
1862         }
1863         if (b->status_format) {
1864             parsed = expand_template(b->status_format, TRUE);
1865             gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1866             g_free(parsed);
1867         }
1868         if (b->status_background) {
1869             GdkColor color;
1870             gdk_color_parse (b->status_background, &color);
1871             //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)
1872             if (uzbl.gui.main_window)
1873                 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1874         }
1875     } else {
1876         if (b->title_format_long) {
1877             parsed = expand_template(b->title_format_long, FALSE);
1878             if (uzbl.gui.main_window)
1879                 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1880             g_free(parsed);
1881         }
1882     }
1883 }
1884
1885 static gboolean
1886 key_press_cb (GtkWidget* window, GdkEventKey* event)
1887 {
1888     //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1889
1890     (void) window;
1891
1892     if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1893         || 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)
1894         return FALSE;
1895
1896     /* turn off insert mode (if always_insert_mode is not used) */
1897     if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1898         uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1899         update_title();
1900         return TRUE;
1901     }
1902
1903     if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1904         return FALSE;
1905
1906     if (event->keyval == GDK_Escape) {
1907         g_string_truncate(uzbl.state.keycmd, 0);
1908         update_title();
1909         dehilight(uzbl.gui.web_view, NULL);
1910         return TRUE;
1911     }
1912
1913     //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1914     if (event->keyval == GDK_Insert) {
1915         gchar * str;
1916         if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1917             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1918         } else {
1919             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1920         }
1921         if (str) {
1922             g_string_append (uzbl.state.keycmd, str);
1923             update_title ();
1924             g_free (str);
1925         }
1926         return TRUE;
1927     }
1928
1929     if (event->keyval == GDK_BackSpace)
1930         keycmd_bs(NULL, NULL);
1931
1932     gboolean key_ret = FALSE;
1933     if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1934         key_ret = TRUE;
1935     if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1936
1937     run_keycmd(key_ret);
1938     update_title();
1939     if (key_ret) return (!uzbl.behave.insert_mode);
1940     return TRUE;
1941 }
1942
1943 static void
1944 run_keycmd(const gboolean key_ret) {
1945     /* run the keycmd immediately if it isn't incremental and doesn't take args */
1946     Action *act;
1947     if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1948         g_string_truncate(uzbl.state.keycmd, 0);
1949         parse_command(act->name, act->param);
1950         return;
1951     }
1952
1953     /* try if it's an incremental keycmd or one that takes args, and run it */
1954     GString* short_keys = g_string_new ("");
1955     GString* short_keys_inc = g_string_new ("");
1956     guint i;
1957     for (i=0; i<(uzbl.state.keycmd->len); i++) {
1958         g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1959         g_string_assign(short_keys_inc, short_keys->str);
1960         g_string_append_c(short_keys, '_');
1961         g_string_append_c(short_keys_inc, '*');
1962
1963         if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1964             /* run normal cmds only if return was pressed */
1965             exec_paramcmd(act, i);
1966             g_string_truncate(uzbl.state.keycmd, 0);
1967             break;
1968         } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1969             if (key_ret)  /* just quit the incremental command on return */
1970                 g_string_truncate(uzbl.state.keycmd, 0);
1971             else exec_paramcmd(act, i); /* otherwise execute the incremental */
1972             break;
1973         }
1974         
1975         g_string_truncate(short_keys, short_keys->len - 1);
1976     }
1977     g_string_free (short_keys, TRUE);
1978     g_string_free (short_keys_inc, TRUE);
1979 }
1980
1981 static void
1982 exec_paramcmd(const Action *act, const guint i) {
1983     GString *parampart = g_string_new (uzbl.state.keycmd->str);
1984     GString *actionname = g_string_new ("");
1985     GString *actionparam = g_string_new ("");
1986     g_string_erase (parampart, 0, i+1);
1987     if (act->name)
1988         g_string_printf (actionname, act->name, parampart->str);
1989     if (act->param)
1990         g_string_printf (actionparam, act->param, parampart->str);
1991     parse_command(actionname->str, actionparam->str);
1992     g_string_free(actionname, TRUE);
1993     g_string_free(actionparam, TRUE);
1994     g_string_free(parampart, TRUE);
1995 }
1996
1997
1998 static GtkWidget*
1999 create_browser () {
2000     GUI *g = &uzbl.gui;
2001
2002     GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2003     //main_window_ref = g_object_ref(scrolled_window);
2004     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_NEVER); //todo: some sort of display of position/total length. like what emacs does
2005
2006     g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2007     gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2008
2009     g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
2010     g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2011     g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2012     g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2013     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2014     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2015     g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2016     g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2017     g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2018     g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2019     g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2020
2021     return scrolled_window;
2022 }
2023
2024 static GtkWidget*
2025 create_mainbar () {
2026     GUI *g = &uzbl.gui;
2027
2028     g->mainbar = gtk_hbox_new (FALSE, 0);
2029
2030     /* keep a reference to the bar so we can re-pack it at runtime*/
2031     //sbar_ref = g_object_ref(g->mainbar);
2032
2033     g->mainbar_label = gtk_label_new ("");
2034     gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2035     gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2036     gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2037     gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2038     gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2039     g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2040     return g->mainbar;
2041 }
2042
2043 static
2044 GtkWidget* create_window () {
2045     GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2046     gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2047     gtk_widget_set_name (window, "Uzbl browser");
2048     g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2049     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2050
2051     return window;
2052 }
2053
2054 static
2055 GtkPlug* create_plug () {
2056     GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2057     g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2058     g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2059
2060     return plug;
2061 }
2062
2063
2064 static gchar**
2065 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2066     /*
2067       If actname is one that calls an external command, this function will inject
2068       newargs in front of the user-provided args in that command line.  They will
2069       come become after the body of the script (in sh) or after the name of
2070       the command to execute (in spawn).
2071       i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2072       span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2073
2074       The return value consist of two strings: the action (sh, ...) and its args.
2075
2076       If act is not one that calls an external command, then the given action merely
2077       gets duplicated.
2078     */
2079     GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2080     gchar *actdup = g_strdup(actname);
2081     g_array_append_val(rets, actdup);
2082
2083     if ((g_strcmp0(actname, "spawn") == 0) ||
2084         (g_strcmp0(actname, "sh") == 0) ||
2085         (g_strcmp0(actname, "sync_spawn") == 0) ||
2086         (g_strcmp0(actname, "sync_sh") == 0)) {
2087         guint i;
2088         GString *a = g_string_new("");
2089         gchar **spawnparts = split_quoted(origargs, FALSE);
2090         g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2091         if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2092
2093         for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2094             if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2095
2096         g_array_append_val(rets, a->str);
2097         g_string_free(a, FALSE);
2098         g_strfreev(spawnparts);
2099     } else {
2100         gchar *origdup = g_strdup(origargs);
2101         g_array_append_val(rets, origdup);
2102     }
2103     return (gchar**)g_array_free(rets, FALSE);
2104 }
2105
2106 static void
2107 run_handler (const gchar *act, const gchar *args) {
2108     /* Consider this code a temporary hack to make the handlers usable.
2109        In practice, all this splicing, injection, and reconstruction is
2110        inefficient, annoying and hard to manage.  Potential pitfalls arise
2111        when the handler specific args 1) are not quoted  (the handler
2112        callbacks should take care of this)  2) are quoted but interfere
2113        with the users' own quotation.  A more ideal solution is
2114        to refactor parse_command so that it doesn't just take a string
2115        and execute it; rather than that, we should have a function which
2116        returns the argument vector parsed from the string.  This vector
2117        could be modified (e.g. insert additional args into it) before
2118        passing it to the next function that actually executes it.  Though
2119        it still isn't perfect for chain actions..  will reconsider & re-
2120        factor when I have the time. -duc */
2121
2122     char **parts = g_strsplit(act, " ", 2);
2123     if (!parts) return;
2124     if (g_strcmp0(parts[0], "chain") == 0) {
2125         GString *newargs = g_string_new("");
2126         gchar **chainparts = split_quoted(parts[1], FALSE);
2127         
2128         /* for every argument in the chain, inject the handler args
2129            and make sure the new parts are wrapped in quotes */
2130         gchar **cp = chainparts;
2131         gchar quot = '\'';
2132         gchar *quotless = NULL;
2133         gchar **spliced_quotless = NULL; // sigh -_-;
2134         gchar **inpart = NULL;
2135         
2136         while (*cp) {
2137             if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2138                 quot = **cp;
2139                 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2140             } else quotless = g_strdup(*cp);
2141
2142             spliced_quotless = g_strsplit(quotless, " ", 2);
2143             inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2144             g_strfreev(spliced_quotless);
2145             
2146             g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2147             g_free(quotless);
2148             g_strfreev(inpart);
2149             cp++;
2150         }
2151
2152         parse_command(parts[0], &(newargs->str[1]));
2153         g_string_free(newargs, TRUE);
2154         g_strfreev(chainparts);
2155         
2156     } else {
2157         gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2158         parse_command(inparts[0], inparts[1]);
2159         g_free(inparts[0]);
2160         g_free(inparts[1]);
2161     }
2162     g_strfreev(parts);
2163 }
2164
2165 static void
2166 add_binding (const gchar *key, const gchar *act) {
2167     char **parts = g_strsplit(act, " ", 2);
2168     Action *action;
2169
2170     if (!parts)
2171         return;
2172
2173     //Debug:
2174     if (uzbl.state.verbose)
2175         printf ("Binding %-10s : %s\n", key, act);
2176     action = new_action(parts[0], parts[1]);
2177
2178     if (g_hash_table_remove (uzbl.bindings, key))
2179         g_warning ("Overwriting existing binding for \"%s\"", key);
2180     g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2181     g_strfreev(parts);
2182 }
2183
2184 static gchar*
2185 get_xdg_var (XDG_Var xdg) {
2186     const gchar* actual_value = getenv (xdg.environmental);
2187     const gchar* home         = getenv ("HOME");
2188
2189     gchar* return_value = str_replace ("~", home, actual_value);
2190
2191     if (! actual_value || strcmp (actual_value, "") == 0) {
2192         if (xdg.default_value) {
2193             return_value = str_replace ("~", home, xdg.default_value);
2194         } else {
2195             return_value = NULL;
2196         }
2197     }
2198     return return_value;
2199 }
2200
2201 static gchar*
2202 find_xdg_file (int xdg_type, char* filename) {
2203     /* xdg_type = 0 => config
2204        xdg_type = 1 => data
2205        xdg_type = 2 => cache*/
2206
2207     gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2208     gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2209     g_free (xdgv);
2210
2211     gchar* temporary_string;
2212     char*  saveptr;
2213     char*  buf;
2214
2215     if (! file_exists (temporary_file) && xdg_type != 2) {
2216         buf = get_xdg_var (XDG[3 + xdg_type]);
2217         temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2218         g_free(buf);
2219
2220         while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2221             g_free (temporary_file);
2222             temporary_file = g_strconcat (temporary_string, filename, NULL);
2223         }
2224     }
2225     
2226     //g_free (temporary_string); - segfaults.
2227
2228     if (file_exists (temporary_file)) {
2229         return temporary_file;
2230     } else {
2231         return NULL;
2232     }
2233 }
2234 static void
2235 settings_init () {
2236     State *s = &uzbl.state;
2237     Network *n = &uzbl.net;
2238     int i;
2239     for (i = 0; default_config[i].command != NULL; i++) {
2240         parse_cmd_line(default_config[i].command);
2241     }
2242
2243     if (!s->config_file) {
2244         s->config_file = find_xdg_file (0, "/uzbl/config");
2245     }
2246
2247     if (s->config_file) {
2248         GArray* lines = read_file_by_line (s->config_file);
2249         int i = 0;
2250         gchar* line;
2251
2252         while ((line = g_array_index(lines, gchar*, i))) {
2253             parse_cmd_line (line);
2254             i ++;
2255             g_free (line);
2256         }
2257         g_array_free (lines, TRUE);
2258     } else {
2259         if (uzbl.state.verbose)
2260             printf ("No configuration file loaded.\n");
2261     }
2262
2263     g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2264 }
2265
2266 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2267     (void) session;
2268     (void) user_data;
2269     if (!uzbl.behave.cookie_handler)
2270          return;
2271
2272     soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2273     GString *s = g_string_new ("");
2274     SoupURI * soup_uri = soup_message_get_uri(msg);
2275     g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2276     run_handler(uzbl.behave.cookie_handler, s->str);
2277
2278     if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2279         char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2280         if ( p != NULL ) *p = '\0';
2281         soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2282     }
2283     if (uzbl.comm.sync_stdout)
2284         uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2285         
2286     g_string_free(s, TRUE);
2287 }
2288
2289 static void
2290 save_cookies (SoupMessage *msg, gpointer user_data){
2291     (void) user_data;
2292     GSList *ck;
2293     char *cookie;
2294     for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2295         cookie = soup_cookie_to_set_cookie_header(ck->data);
2296         SoupURI * soup_uri = soup_message_get_uri(msg);
2297         GString *s = g_string_new ("");
2298         g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2299         run_handler(uzbl.behave.cookie_handler, s->str);
2300         g_free (cookie);
2301         g_string_free(s, TRUE);
2302     }
2303     g_slist_free(ck);
2304 }
2305
2306 /* --- WEBINSPECTOR --- */
2307 static void
2308 hide_window_cb(GtkWidget *widget, gpointer data) {
2309     (void) data;
2310
2311     gtk_widget_hide(widget);
2312 }
2313
2314 static WebKitWebView*
2315 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2316     (void) data;
2317     (void) page;
2318     (void) web_inspector;
2319     GtkWidget* scrolled_window;
2320     GtkWidget* new_web_view;
2321     GUI *g = &uzbl.gui;
2322
2323     g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2324     g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2325             G_CALLBACK(hide_window_cb), NULL);
2326
2327     gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2328     gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2329     gtk_widget_show(g->inspector_window);
2330
2331     scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2332     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2333             GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2334     gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2335     gtk_widget_show(scrolled_window);
2336
2337     new_web_view = webkit_web_view_new();
2338     gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2339
2340     return WEBKIT_WEB_VIEW(new_web_view);
2341 }
2342
2343 static gboolean
2344 inspector_show_window_cb (WebKitWebInspector* inspector){
2345     (void) inspector;
2346     gtk_widget_show(uzbl.gui.inspector_window);
2347     return TRUE;
2348 }
2349
2350 /* TODO: Add variables and code to make use of these functions */
2351 static gboolean
2352 inspector_close_window_cb (WebKitWebInspector* inspector){
2353     (void) inspector;
2354     return TRUE;
2355 }
2356
2357 static gboolean
2358 inspector_attach_window_cb (WebKitWebInspector* inspector){
2359     (void) inspector;
2360     return FALSE;
2361 }
2362
2363 static gboolean
2364 inspector_dettach_window_cb (WebKitWebInspector* inspector){
2365     (void) inspector;
2366     return FALSE;
2367 }
2368
2369 static gboolean
2370 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2371     (void) inspector;
2372     return FALSE;
2373 }
2374
2375 static gboolean
2376 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2377     (void) inspector;
2378     return FALSE;
2379 }
2380
2381 static void
2382 set_up_inspector() {
2383     GUI *g = &uzbl.gui;
2384     WebKitWebSettings *settings = view_settings();
2385     g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2386
2387     uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2388     g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2389     g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2390     g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2391     g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2392     if (uzbl.gui.main_window) {
2393         g_signal_connect (G_OBJECT (g->inspector), "dettach-window", G_CALLBACK (inspector_dettach_window_cb), NULL);
2394         g_signal_connect (G_OBJECT (g->inspector), "destroy", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2395     }
2396
2397     g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2398 }
2399
2400 static void
2401 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2402     (void) ud;
2403     uzbl_cmdprop *c = v;
2404
2405     if(!c->dump)
2406         return;
2407
2408     if(c->type == TYPE_STR)
2409         printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2410     else if(c->type == TYPE_INT)
2411         printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2412 }
2413
2414 static void
2415 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2416     (void) ud;
2417     Action *a = v;
2418
2419     printf("bind %s = %s %s\n", (char *)k ,
2420             (char *)a->name, a->param?(char *)a->param:"");
2421 }
2422
2423 static void
2424 dump_config() {
2425     g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2426     g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2427 }
2428
2429 /** -- MAIN -- **/
2430 int
2431 main (int argc, char* argv[]) {
2432     gtk_init (&argc, &argv);
2433     if (!g_thread_supported ())
2434         g_thread_init (NULL);
2435     uzbl.state.executable_path = g_strdup(argv[0]);
2436     uzbl.state.selected_url = NULL;
2437     uzbl.state.searchtx = NULL;
2438
2439     GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2440     g_option_context_add_main_entries (context, entries, NULL);
2441     g_option_context_add_group (context, gtk_get_option_group (TRUE));
2442     g_option_context_parse (context, &argc, &argv, NULL);
2443     g_option_context_free(context);
2444     
2445     gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2446     gboolean verbose_override = uzbl.state.verbose;
2447
2448     /* initialize hash table */
2449     uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2450
2451     uzbl.net.soup_session = webkit_get_default_session();
2452     uzbl.state.keycmd = g_string_new("");
2453
2454     if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2455         fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2456     if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2457         fprintf(stderr, "uzbl: error hooking SIGINT\n");
2458     if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2459         fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2460
2461
2462     if(uname(&uzbl.state.unameinfo) == -1)
2463         g_printerr("Can't retrieve unameinfo.  Your useragent might appear wrong.\n");
2464
2465     uzbl.gui.sbar.progress_s = g_strdup("=");
2466     uzbl.gui.sbar.progress_u = g_strdup("ยท");
2467     uzbl.gui.sbar.progress_w = 10;
2468
2469     /* HTML mode defaults*/
2470     uzbl.behave.html_buffer = g_string_new("");
2471     uzbl.behave.html_endmarker = g_strdup(".");
2472     uzbl.behave.html_timeout = 60;
2473     uzbl.behave.base_url = g_strdup("http://invalid");
2474
2475     /* default mode indicators */
2476     uzbl.behave.insert_indicator = g_strdup("I");
2477     uzbl.behave.cmd_indicator    = g_strdup("C");
2478
2479     setup_scanner();
2480     commands_hash ();
2481     make_var_to_name_hash();
2482
2483     uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2484
2485     uzbl.gui.scrolled_win = create_browser();
2486     create_mainbar();
2487
2488     /* initial packing */
2489     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2490     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2491
2492     if (uzbl.state.socket_id) {
2493         uzbl.gui.plug = create_plug ();
2494         gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2495         gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2496     } else {
2497         uzbl.gui.main_window = create_window ();
2498         gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2499         gtk_widget_show_all (uzbl.gui.main_window);
2500         uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2501     }
2502
2503     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2504
2505     if (uzbl.state.verbose) {
2506         printf("Uzbl start location: %s\n", argv[0]);
2507         if (uzbl.state.socket_id)
2508             printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2509         else
2510             printf("window_id %i\n",(int) uzbl.xwin);
2511         printf("pid %i\n", getpid ());
2512         printf("name: %s\n", uzbl.state.instance_name);
2513     }
2514
2515     uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2516     uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2517     uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2518     uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2519     gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2520
2521     settings_init ();
2522
2523     if (!uzbl.behave.show_status)
2524         gtk_widget_hide(uzbl.gui.mainbar);
2525     else
2526         update_title();
2527
2528     /* WebInspector */
2529     set_up_inspector();
2530
2531     create_stdin();
2532
2533     if (verbose_override > uzbl.state.verbose)
2534         uzbl.state.verbose = verbose_override;
2535     
2536     if (uri_override) {
2537         set_var_value("uri", uri_override);
2538         g_free(uri_override);
2539     } else if (uzbl.state.uri)
2540         cmd_load_uri(uzbl.gui.web_view, NULL);
2541
2542     gtk_main ();
2543     clean_up();
2544
2545     return EXIT_SUCCESS;
2546 }
2547
2548 /* vi: set et ts=4: */