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