s/cookie_file/cookie_dir/g
[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_strrstr (argv_idx(argv, 0), "://") == NULL)
735             g_string_prepend (newuri, "http://");
736         /* if we do handle cookies, ask our handler for them */
737         webkit_web_view_load_uri (web_view, newuri->str);
738         g_string_free (newuri, TRUE);
739     }
740 }
741
742 static void
743 run_js (WebKitWebView * web_view, GArray *argv) {
744     if (argv_idx(argv, 0))
745         webkit_web_view_execute_script (web_view, argv_idx(argv, 0));
746 }
747
748 static void
749 run_external_js (WebKitWebView * web_view, GArray *argv) {
750     if (argv_idx(argv, 0)) {
751         GArray* lines = read_file_by_line (argv_idx (argv, 0));
752         gchar*  js = NULL;
753         int i = 0;
754         gchar* line;
755
756         while ((line = g_array_index(lines, gchar*, i))) {
757             if (js == NULL) {
758                 js = g_strdup (line);
759             } else {
760                 gchar* newjs = g_strconcat (js, line, NULL);
761                 js = newjs;
762             }
763             i ++;
764             g_free (line);
765         }
766         
767         if (uzbl.state.verbose)
768             printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
769
770         if (argv_idx (argv, 1)) {
771             gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
772             g_free (js);
773             js = newjs;
774         }
775         webkit_web_view_execute_script (web_view, js);
776         g_free (js);
777         g_array_free (lines, TRUE);
778     }
779 }
780
781 static void
782 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
783     if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
784         if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
785             webkit_web_view_unmark_text_matches (page);
786             webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
787             uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
788         }
789     }
790     
791     if (uzbl.state.searchtx) {
792         if (uzbl.state.verbose)
793             printf ("Searching: %s\n", uzbl.state.searchtx);
794         webkit_web_view_set_highlight_text_matches (page, TRUE);
795         webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
796     }
797 }
798
799 static void
800 search_forward_text (WebKitWebView *page, GArray *argv) {
801     search_text(page, argv, TRUE);
802 }
803
804 static void
805 search_reverse_text (WebKitWebView *page, GArray *argv) {
806     search_text(page, argv, FALSE);
807 }
808
809 static void
810 dehilight (WebKitWebView *page, GArray *argv) {
811     (void) argv;
812     webkit_web_view_set_highlight_text_matches (page, FALSE);
813 }
814
815
816 static void
817 new_window_load_uri (const gchar * uri) {
818     GString* to_execute = g_string_new ("");
819     g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
820     int i;
821     for (i = 0; entries[i].long_name != NULL; i++) {
822         if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
823             gchar** str = (gchar**)entries[i].arg_data;
824             if (*str!=NULL) {
825                 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
826             }
827         }
828     }
829     if (uzbl.state.verbose)
830         printf("\n%s\n", to_execute->str);
831     g_spawn_command_line_async (to_execute->str, NULL);
832     g_string_free (to_execute, TRUE);
833 }
834
835 static void
836 chain (WebKitWebView *page, GArray *argv) {
837     (void)page;
838     gchar *a = NULL;
839     gchar **parts = NULL;
840     guint i = 0;    
841     while ((a = argv_idx(argv, i++))) {
842         parts = g_strsplit (a, " ", 2);
843         parse_command(parts[0], parts[1]);
844         g_strfreev (parts);
845     }
846 }
847
848 static void
849 keycmd (WebKitWebView *page, GArray *argv) {
850     (void)page;
851     (void)argv;
852     g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
853     run_keycmd(FALSE);
854     update_title();
855 }
856
857 static void
858 keycmd_nl (WebKitWebView *page, GArray *argv) {
859     (void)page;
860     (void)argv;
861     g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
862     run_keycmd(TRUE);
863     update_title();
864 }
865
866 static void
867 keycmd_bs (WebKitWebView *page, GArray *argv) {
868     (void)page;
869     (void)argv;
870     g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
871     update_title();
872 }
873
874 static void
875 close_uzbl (WebKitWebView *page, GArray *argv) {
876     (void)page;
877     (void)argv;
878     gtk_main_quit ();
879 }
880
881 /* --Statusbar functions-- */
882 static char*
883 build_progressbar_ascii(int percent) {
884    int width=uzbl.gui.sbar.progress_w;
885    int i;
886    double l;
887    GString *bar = g_string_new("");
888
889    l = (double)percent*((double)width/100.);
890    l = (int)(l+.5)>=(int)l ? l+.5 : l;
891
892    for(i=0; i<(int)l; i++)
893        g_string_append(bar, uzbl.gui.sbar.progress_s);
894
895    for(; i<width; i++)
896        g_string_append(bar, uzbl.gui.sbar.progress_u);
897
898    return g_string_free(bar, FALSE);
899 }
900
901 static void
902 setup_scanner() {
903      const GScannerConfig scan_config = {
904              (
905               "\t\r\n"
906              )            /* cset_skip_characters */,
907              (
908               G_CSET_a_2_z
909               "_#"
910               G_CSET_A_2_Z
911              )            /* cset_identifier_first */,
912              (
913               G_CSET_a_2_z
914               "_0123456789"
915               G_CSET_A_2_Z
916               G_CSET_LATINS
917               G_CSET_LATINC
918              )            /* cset_identifier_nth */,
919              ( "" )    /* cpair_comment_single */,
920
921              TRUE         /* case_sensitive */,
922
923              FALSE        /* skip_comment_multi */,
924              FALSE        /* skip_comment_single */,
925              FALSE        /* scan_comment_multi */,
926              TRUE         /* scan_identifier */,
927              TRUE         /* scan_identifier_1char */,
928              FALSE        /* scan_identifier_NULL */,
929              TRUE         /* scan_symbols */,
930              FALSE        /* scan_binary */,
931              FALSE        /* scan_octal */,
932              FALSE        /* scan_float */,
933              FALSE        /* scan_hex */,
934              FALSE        /* scan_hex_dollar */,
935              FALSE        /* scan_string_sq */,
936              FALSE        /* scan_string_dq */,
937              TRUE         /* numbers_2_int */,
938              FALSE        /* int_2_float */,
939              FALSE        /* identifier_2_string */,
940              FALSE        /* char_2_token */,
941              FALSE        /* symbol_2_token */,
942              TRUE         /* scope_0_fallback */,
943              FALSE,
944              TRUE
945      };
946
947      uzbl.scan = g_scanner_new(&scan_config);
948      while(symp->symbol_name) {
949          g_scanner_scope_add_symbol(uzbl.scan, 0,
950                          symp->symbol_name,
951                          GINT_TO_POINTER(symp->symbol_token));
952          symp++;
953      }
954 }
955
956 static gchar *
957 expand_template(const char *template, gboolean escape_markup) {
958      if(!template) return NULL;
959
960      GTokenType token = G_TOKEN_NONE;
961      GString *ret = g_string_new("");
962      char *buf=NULL;
963      int sym;
964
965      g_scanner_input_text(uzbl.scan, template, strlen(template));
966      while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
967          token = g_scanner_get_next_token(uzbl.scan);
968
969          if(token == G_TOKEN_SYMBOL) {
970              sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
971              switch(sym) {
972                  case SYM_URI:
973                      if(escape_markup) {
974                          buf = uzbl.state.uri?
975                              g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
976                          g_string_append(ret, buf);
977                          g_free(buf);
978                      }
979                      else
980                          g_string_append(ret, uzbl.state.uri?
981                                  uzbl.state.uri:g_strdup(""));
982                      break;
983                  case SYM_LOADPRGS:
984                      buf = itos(uzbl.gui.sbar.load_progress);
985                      g_string_append(ret, buf);
986                      g_free(buf);
987                      break;
988                  case SYM_LOADPRGSBAR:
989                      buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
990                      g_string_append(ret, buf);
991                      g_free(buf);
992                      break;
993                  case SYM_TITLE:
994                      if(escape_markup) {
995                          buf = uzbl.gui.main_title?
996                              g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
997                          g_string_append(ret, buf);
998                          g_free(buf);
999                      }
1000                      else
1001                          g_string_append(ret, uzbl.gui.main_title?
1002                                  uzbl.gui.main_title:g_strdup(""));
1003                      break;
1004                  case SYM_SELECTED_URI:
1005                      if(escape_markup) {
1006                          buf = uzbl.state.selected_url?
1007                              g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
1008                          g_string_append(ret, buf);
1009                          g_free(buf);
1010                      }
1011                      else
1012                          g_string_append(ret, uzbl.state.selected_url?
1013                                  uzbl.state.selected_url:g_strdup(""));
1014                      break;
1015                  case SYM_NAME:
1016                      buf = itos(uzbl.xwin);
1017                      g_string_append(ret,
1018                              uzbl.state.instance_name?uzbl.state.instance_name:buf);
1019                      g_free(buf);
1020                      break;
1021                  case SYM_KEYCMD:
1022                      if(escape_markup) {
1023                          buf = uzbl.state.keycmd->str?
1024                              g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1025                          g_string_append(ret, buf);
1026                          g_free(buf);
1027                      }
1028                      else
1029                          g_string_append(ret, uzbl.state.keycmd->str?
1030                                  uzbl.state.keycmd->str:g_strdup(""));
1031                      break;
1032                  case SYM_MODE:
1033                      g_string_append(ret,
1034                              uzbl.behave.insert_mode?
1035                              uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1036                      break;
1037                  case SYM_MSG:
1038                      g_string_append(ret,
1039                              uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1040                      break;
1041                      /* useragent syms */
1042                  case SYM_WK_MAJ:
1043                      buf = itos(WEBKIT_MAJOR_VERSION);
1044                      g_string_append(ret, buf);
1045                      g_free(buf);
1046                      break;
1047                  case SYM_WK_MIN:
1048                      buf = itos(WEBKIT_MINOR_VERSION);
1049                      g_string_append(ret, buf);
1050                      g_free(buf);
1051                      break;
1052                  case SYM_WK_MIC:
1053                      buf = itos(WEBKIT_MICRO_VERSION);
1054                      g_string_append(ret, buf);
1055                      g_free(buf);
1056                      break;
1057                  case SYM_SYSNAME:
1058                      g_string_append(ret, uzbl.state.unameinfo.sysname);
1059                      break;
1060                  case SYM_NODENAME:
1061                      g_string_append(ret, uzbl.state.unameinfo.nodename);
1062                      break;
1063                  case SYM_KERNREL:
1064                      g_string_append(ret, uzbl.state.unameinfo.release);
1065                      break;
1066                  case SYM_KERNVER:
1067                      g_string_append(ret, uzbl.state.unameinfo.version);
1068                      break;
1069                  case SYM_ARCHSYS:
1070                      g_string_append(ret, uzbl.state.unameinfo.machine);
1071                      break;
1072                  case SYM_ARCHUZBL:
1073                      g_string_append(ret, ARCH);
1074                      break;
1075 #ifdef _GNU_SOURCE
1076                  case SYM_DOMAINNAME:
1077                      g_string_append(ret, uzbl.state.unameinfo.domainname);
1078                      break;
1079 #endif
1080                  case SYM_COMMIT:
1081                      g_string_append(ret, COMMIT);
1082                      break;
1083                  default:
1084                      break;
1085              }
1086          }
1087          else if(token == G_TOKEN_INT) {
1088              buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1089              g_string_append(ret, buf);
1090              g_free(buf);
1091          }
1092          else if(token == G_TOKEN_IDENTIFIER) {
1093              g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1094          }
1095          else if(token == G_TOKEN_CHAR) {
1096              g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1097          }
1098      }
1099
1100      return g_string_free(ret, FALSE);
1101 }
1102 /* --End Statusbar functions-- */
1103
1104 static void
1105 sharg_append(GArray *a, const gchar *str) {
1106     const gchar *s = (str ? str : "");
1107     g_array_append_val(a, s);
1108 }
1109
1110 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1111 static gboolean
1112 run_command (const gchar *command, const guint npre, const gchar **args,
1113              const gboolean sync, char **stdout) {
1114    //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1115     GError *err = NULL;
1116     
1117     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1118     gchar *pid = itos(getpid());
1119     gchar *xwin = itos(uzbl.xwin);
1120     guint i;
1121     sharg_append(a, command);
1122     for (i = 0; i < npre; i++) /* add n args before the default vars */
1123         sharg_append(a, args[i]);
1124     sharg_append(a, uzbl.state.config_file);
1125     sharg_append(a, pid);
1126     sharg_append(a, xwin);
1127     sharg_append(a, uzbl.comm.fifo_path);
1128     sharg_append(a, uzbl.comm.socket_path);
1129     sharg_append(a, uzbl.state.uri);
1130     sharg_append(a, uzbl.gui.main_title);
1131
1132     for (i = npre; i < g_strv_length((gchar**)args); i++)
1133         sharg_append(a, args[i]);
1134     
1135     gboolean result;
1136     if (sync) {
1137         if (*stdout) *stdout = strfree(*stdout);
1138         
1139         result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1140                               NULL, NULL, stdout, NULL, NULL, &err);
1141     } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1142                                   NULL, NULL, NULL, &err);
1143
1144     if (uzbl.state.verbose) {
1145         GString *s = g_string_new("spawned:");
1146         for (i = 0; i < (a->len); i++) {
1147             gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1148             g_string_append_printf(s, " %s", qarg);
1149             g_free (qarg);
1150         }
1151         g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1152         printf("%s\n", s->str);
1153         g_string_free(s, TRUE);
1154         if(stdout) {
1155             printf("Stdout: %s\n", *stdout);
1156         }
1157     }
1158     if (err) {
1159         g_printerr("error on run_command: %s\n", err->message);
1160         g_error_free (err);
1161     }
1162     g_free (pid);
1163     g_free (xwin);
1164     g_array_free (a, TRUE);
1165     return result;
1166 }
1167
1168 static gchar**
1169 split_quoted(const gchar* src, const gboolean unquote) {
1170     /* split on unquoted space, return array of strings;
1171        remove a layer of quotes and backslashes if unquote */
1172     if (!src) return NULL;
1173     
1174     gboolean dq = FALSE;
1175     gboolean sq = FALSE;
1176     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1177     GString *s = g_string_new ("");
1178     const gchar *p;
1179     gchar **ret;
1180     gchar *dup;
1181     for (p = src; *p != '\0'; p++) {
1182         if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1183         else if (*p == '\\') { g_string_append_c(s, *p++);
1184                                g_string_append_c(s, *p); }
1185         else if ((*p == '"') && unquote && !sq) dq = !dq;
1186         else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1187                                      dq = !dq; }
1188         else if ((*p == '\'') && unquote && !dq) sq = !sq;
1189         else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1190                                       sq = ! sq; }
1191         else if ((*p == ' ') && !dq && !sq) {
1192             dup = g_strdup(s->str);
1193             g_array_append_val(a, dup);
1194             g_string_truncate(s, 0);
1195         } else g_string_append_c(s, *p);
1196     }
1197     dup = g_strdup(s->str);
1198     g_array_append_val(a, dup);
1199     ret = (gchar**)a->data;
1200     g_array_free (a, FALSE);
1201     g_string_free (s, TRUE);
1202     return ret;
1203 }
1204
1205 static void
1206 spawn(WebKitWebView *web_view, GArray *argv) {
1207     (void)web_view;
1208     //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1209     if (argv_idx(argv, 0))
1210         run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1211 }
1212
1213 static void
1214 spawn_sync(WebKitWebView *web_view, GArray *argv) {
1215     (void)web_view;
1216     
1217     if (argv_idx(argv, 0))
1218         run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1219                     TRUE, &uzbl.comm.sync_stdout);
1220 }
1221
1222 static void
1223 spawn_sh(WebKitWebView *web_view, GArray *argv) {
1224     (void)web_view;
1225     if (!uzbl.behave.shell_cmd) {
1226         g_printerr ("spawn_sh: shell_cmd is not set!\n");
1227         return;
1228     }
1229     
1230     guint i;
1231     gchar *spacer = g_strdup("");
1232     g_array_insert_val(argv, 1, spacer);
1233     gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1234
1235     for (i = 1; i < g_strv_length(cmd); i++)
1236         g_array_prepend_val(argv, cmd[i]);
1237
1238     if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1239     g_free (spacer);
1240     g_strfreev (cmd);
1241 }
1242
1243 static void
1244 spawn_sh_sync(WebKitWebView *web_view, GArray *argv) {
1245     (void)web_view;
1246     if (!uzbl.behave.shell_cmd) {
1247         g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1248         return;
1249     }
1250     
1251     guint i;
1252     gchar *spacer = g_strdup("");
1253     g_array_insert_val(argv, 1, spacer);
1254     gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1255
1256     for (i = 1; i < g_strv_length(cmd); i++)
1257         g_array_prepend_val(argv, cmd[i]);
1258          
1259     if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1260                          TRUE, &uzbl.comm.sync_stdout);
1261     g_free (spacer);
1262     g_strfreev (cmd);
1263 }
1264
1265 static void
1266 parse_command(const char *cmd, const char *param) {
1267     Command *c;
1268
1269     if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1270
1271             guint i;
1272             gchar **par = split_quoted(param, TRUE);
1273             GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1274
1275             if (c[1] == NOSPLIT) { /* don't split */
1276                 sharg_append(a, param);
1277             } else if (par) {
1278                 for (i = 0; i < g_strv_length(par); i++)
1279                     sharg_append(a, par[i]);
1280             }
1281             c[0](uzbl.gui.web_view, a);
1282             g_strfreev (par);
1283             g_array_free (a, TRUE);
1284
1285     } else
1286         g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1287 }
1288
1289 static void
1290 set_proxy_url() {
1291     SoupURI *suri;
1292
1293     if(*uzbl.net.proxy_url == ' '
1294        || uzbl.net.proxy_url == NULL) {
1295         soup_session_remove_feature_by_type(uzbl.net.soup_session,
1296                 (GType) SOUP_SESSION_PROXY_URI);
1297     }
1298     else {
1299         suri = soup_uri_new(uzbl.net.proxy_url);
1300         g_object_set(G_OBJECT(uzbl.net.soup_session),
1301                 SOUP_SESSION_PROXY_URI,
1302                 suri, NULL);
1303         soup_uri_free(suri);
1304     }
1305     return;
1306 }
1307
1308 static void
1309 cmd_load_uri() {
1310     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1311     g_array_append_val (a, uzbl.state.uri);
1312     load_uri(uzbl.gui.web_view, a);
1313     g_array_free (a, TRUE);
1314 }
1315
1316 static void 
1317 cmd_always_insert_mode() {
1318     uzbl.behave.insert_mode =
1319         uzbl.behave.always_insert_mode ?  TRUE : FALSE;
1320     update_title();
1321 }
1322
1323 static void
1324 cmd_max_conns() {
1325     g_object_set(G_OBJECT(uzbl.net.soup_session),
1326             SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1327 }
1328
1329 static void
1330 cmd_max_conns_host() {
1331     g_object_set(G_OBJECT(uzbl.net.soup_session),
1332             SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1333 }
1334
1335 static void
1336 cmd_http_debug() {
1337     soup_session_remove_feature
1338         (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1339     /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1340     /*g_free(uzbl.net.soup_logger);*/
1341
1342     uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1343     soup_session_add_feature(uzbl.net.soup_session,
1344             SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1345 }
1346
1347 static WebKitWebSettings*
1348 view_settings() {
1349     return webkit_web_view_get_settings(uzbl.gui.web_view);
1350 }
1351
1352 static void
1353 cmd_font_size() {
1354     WebKitWebSettings *ws = view_settings();
1355     if (uzbl.behave.font_size > 0) {
1356         g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1357     }
1358     
1359     if (uzbl.behave.monospace_size > 0) {
1360         g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1361                       uzbl.behave.monospace_size, NULL);
1362     } else {
1363         g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1364                       uzbl.behave.font_size, NULL);
1365     }
1366 }
1367
1368 static void
1369 cmd_zoom_level() {
1370     webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1371 }
1372
1373 static void
1374 cmd_disable_plugins() {
1375     g_object_set (G_OBJECT(view_settings()), "enable-plugins", 
1376             !uzbl.behave.disable_plugins, NULL);
1377 }
1378
1379 static void
1380 cmd_disable_scripts() {
1381     g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1382             !uzbl.behave.disable_scripts, NULL);
1383 }
1384
1385 static void
1386 cmd_minimum_font_size() {
1387     g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1388             uzbl.behave.minimum_font_size, NULL);
1389 }
1390 static void
1391 cmd_autoload_img() {
1392     g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1393             uzbl.behave.autoload_img, NULL);
1394 }
1395
1396
1397 static void
1398 cmd_autoshrink_img() {
1399     g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1400             uzbl.behave.autoshrink_img, NULL);
1401 }
1402
1403
1404 static void
1405 cmd_enable_spellcheck() {
1406     g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1407             uzbl.behave.enable_spellcheck, NULL);
1408 }
1409
1410 static void
1411 cmd_enable_private() {
1412     g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1413             uzbl.behave.enable_private, NULL);
1414 }
1415
1416 static void
1417 cmd_print_bg() {
1418     g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1419             uzbl.behave.print_bg, NULL);
1420 }
1421
1422 static void 
1423 cmd_style_uri() {
1424     g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1425             uzbl.behave.style_uri, NULL);
1426 }
1427
1428 static void 
1429 cmd_resizable_txt() {
1430     g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1431             uzbl.behave.resizable_txt, NULL);
1432 }
1433
1434 static void 
1435 cmd_default_encoding() {
1436     g_object_set (G_OBJECT(view_settings()), "default-encoding",
1437             uzbl.behave.default_encoding, NULL);
1438 }
1439
1440 static void 
1441 cmd_enforce_96dpi() {
1442     g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1443             uzbl.behave.enforce_96dpi, NULL);
1444 }
1445
1446 static void 
1447 cmd_caret_browsing() {
1448     g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1449             uzbl.behave.caret_browsing, NULL);
1450 }
1451
1452 static void
1453 cmd_cookie_handler() {
1454     gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1455     /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1456     if ((g_strcmp0(split[0], "sh") == 0) ||
1457         (g_strcmp0(split[0], "spawn") == 0)) {
1458         g_free (uzbl.behave.cookie_handler);
1459         uzbl.behave.cookie_handler =
1460             g_strdup_printf("sync_%s %s", split[0], split[1]);
1461     }
1462     g_strfreev (split);
1463 }
1464
1465 static void
1466 cmd_fifo_dir() {
1467     uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1468 }
1469
1470 static void
1471 cmd_socket_dir() {
1472     uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1473 }
1474
1475 static void
1476 cmd_inject_html() {
1477     if(uzbl.behave.inject_html) {
1478         webkit_web_view_load_html_string (uzbl.gui.web_view,
1479                 uzbl.behave.inject_html, NULL);
1480     }
1481 }
1482
1483 static void
1484 cmd_modkey() {
1485     int i;
1486     char *buf;
1487
1488     buf = g_utf8_strup(uzbl.behave.modkey, -1);
1489     uzbl.behave.modmask = 0;
1490
1491     if(uzbl.behave.modkey) 
1492         g_free(uzbl.behave.modkey);
1493     uzbl.behave.modkey = buf;
1494
1495     for (i = 0; modkeys[i].key != NULL; i++) {
1496         if (g_strrstr(buf, modkeys[i].key))
1497             uzbl.behave.modmask |= modkeys[i].mask;
1498     }
1499 }
1500
1501 static void
1502 cmd_useragent() {
1503     if (*uzbl.net.useragent == ' ') {
1504         g_free (uzbl.net.useragent);
1505         uzbl.net.useragent = NULL;
1506     } else {
1507         gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1508         if (ua)
1509             g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1510         g_free(uzbl.net.useragent);
1511         uzbl.net.useragent = ua;
1512     }
1513 }
1514
1515 static void
1516 move_statusbar() {
1517     gtk_widget_ref(uzbl.gui.scrolled_win);
1518     gtk_widget_ref(uzbl.gui.mainbar);
1519     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1520     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1521
1522     if(uzbl.behave.status_top) {
1523         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1524         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1525     }
1526     else {
1527         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1528         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1529     }
1530     gtk_widget_unref(uzbl.gui.scrolled_win);
1531     gtk_widget_unref(uzbl.gui.mainbar);
1532     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1533     return;
1534 }
1535
1536 static gboolean
1537 set_var_value(gchar *name, gchar *val) {
1538     uzbl_cmdprop *c = NULL;
1539     char *endp = NULL;
1540     char *buf = NULL;
1541
1542     if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1543         /* check for the variable type */
1544         if (c->type == TYPE_STR) {
1545             buf = expand_vars(val);
1546             g_free(*c->ptr);
1547             *c->ptr = buf;
1548         } else if(c->type == TYPE_INT) {
1549             int *ip = (int *)c->ptr;
1550             buf = expand_vars(val);
1551             *ip = (int)strtoul(buf, &endp, 10);
1552             g_free(buf);
1553         } else if (c->type == TYPE_FLOAT) {
1554             float *fp = (float *)c->ptr;
1555             buf = expand_vars(val);
1556             *fp = strtof(buf, &endp);
1557             g_free(buf);
1558         }
1559
1560         /* invoke a command specific function */
1561         if(c->func) c->func();
1562     }
1563     return TRUE;
1564 }
1565
1566 static void
1567 render_html() {
1568     Behaviour *b = &uzbl.behave;
1569
1570     if(b->html_buffer->str) {
1571         webkit_web_view_load_html_string (uzbl.gui.web_view,
1572                 b->html_buffer->str, b->base_url);
1573         g_string_free(b->html_buffer, TRUE);
1574         b->html_buffer = g_string_new("");
1575     }
1576 }
1577
1578 enum {M_CMD, M_HTML};
1579 static void
1580 parse_cmd_line(const char *ctl_line) {
1581     Behaviour *b = &uzbl.behave;
1582     size_t len=0;
1583
1584     if(b->mode == M_HTML) {
1585         len = strlen(b->html_endmarker);
1586         /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1587         if(len == strlen(ctl_line)-1 &&
1588            !strncmp(b->html_endmarker, ctl_line, len)) {
1589             set_timeout(0);
1590             set_var_value("mode", "0");
1591             render_html();
1592             return;
1593         }
1594         else {
1595             set_timeout(b->html_timeout);
1596             g_string_append(b->html_buffer, ctl_line);
1597         }
1598     }
1599     else if((ctl_line[0] == '#') /* Comments */
1600             || (ctl_line[0] == ' ')
1601             || (ctl_line[0] == '\n'))
1602         ; /* ignore these lines */
1603     else { /* parse a command */
1604         gchar *ctlstrip;
1605         gchar **tokens = NULL;
1606         len = strlen(ctl_line);
1607
1608         if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1609             ctlstrip = g_strndup(ctl_line, len - 1);
1610         else ctlstrip = g_strdup(ctl_line);
1611
1612         tokens = g_strsplit(ctlstrip, " ", 2);
1613         parse_command(tokens[0], tokens[1]);
1614         g_free(ctlstrip);
1615         g_strfreev(tokens);
1616     }
1617 }
1618
1619 static gchar*
1620 build_stream_name(int type, const gchar* dir) {
1621     char *xwin_str;
1622     State *s = &uzbl.state;
1623     gchar *str;
1624
1625     xwin_str = itos((int)uzbl.xwin);
1626     if (type == FIFO) {
1627         str = g_strdup_printf
1628             ("%s/uzbl_fifo_%s", dir,
1629              s->instance_name ? s->instance_name : xwin_str);
1630     } else if (type == SOCKET) {
1631         str = g_strdup_printf
1632             ("%s/uzbl_socket_%s", dir,
1633              s->instance_name ? s->instance_name : xwin_str );
1634     }
1635     g_free(xwin_str);
1636     return str;
1637 }
1638
1639 static gboolean
1640 control_fifo(GIOChannel *gio, GIOCondition condition) {
1641     if (uzbl.state.verbose)
1642         printf("triggered\n");
1643     gchar *ctl_line;
1644     GIOStatus ret;
1645     GError *err = NULL;
1646
1647     if (condition & G_IO_HUP)
1648         g_error ("Fifo: Read end of pipe died!\n");
1649
1650     if(!gio)
1651        g_error ("Fifo: GIOChannel broke\n");
1652
1653     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1654     if (ret == G_IO_STATUS_ERROR) {
1655         g_error ("Fifo: Error reading: %s\n", err->message);
1656         g_error_free (err);
1657     }
1658
1659     parse_cmd_line(ctl_line);
1660     g_free(ctl_line);
1661
1662     return TRUE;
1663 }
1664
1665 static gchar*
1666 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1667     if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1668         if (unlink(uzbl.comm.fifo_path) == -1)
1669             g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1670         g_free(uzbl.comm.fifo_path);
1671         uzbl.comm.fifo_path = NULL;
1672     }
1673
1674     if (*dir == ' ') { /* space unsets the variable */
1675         g_free (dir);
1676         return NULL;
1677     }
1678
1679     GIOChannel *chan = NULL;
1680     GError *error = NULL;
1681     gchar *path = build_stream_name(FIFO, dir);
1682
1683     if (!file_exists(path)) {
1684         if (mkfifo (path, 0666) == 0) {
1685             // 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.
1686             chan = g_io_channel_new_file(path, "r+", &error);
1687             if (chan) {
1688                 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1689                     if (uzbl.state.verbose)
1690                         printf ("init_fifo: created successfully as %s\n", path);
1691                     uzbl.comm.fifo_path = path;
1692                     return dir;
1693                 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1694             } else g_warning ("init_fifo: can't open: %s\n", error->message);
1695         } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1696     } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1697
1698     /* if we got this far, there was an error; cleanup */
1699     if (error) g_error_free (error);
1700     g_free(dir);
1701     g_free(path);
1702     return NULL;
1703 }
1704
1705 static gboolean
1706 control_stdin(GIOChannel *gio, GIOCondition condition) {
1707     (void) condition;
1708     gchar *ctl_line = NULL;
1709     GIOStatus ret;
1710
1711     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1712     if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1713         return FALSE;
1714
1715     parse_cmd_line(ctl_line);
1716     g_free(ctl_line);
1717
1718     return TRUE;
1719 }
1720
1721 static void
1722 create_stdin () {
1723     GIOChannel *chan = NULL;
1724     GError *error = NULL;
1725
1726     chan = g_io_channel_unix_new(fileno(stdin));
1727     if (chan) {
1728         if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1729             g_error ("Stdin: could not add watch\n");
1730         } else {
1731             if (uzbl.state.verbose)
1732                 printf ("Stdin: watch added successfully\n");
1733         }
1734     } else {
1735         g_error ("Stdin: Error while opening: %s\n", error->message);
1736     }
1737     if (error) g_error_free (error);
1738 }
1739
1740 static gboolean
1741 control_socket(GIOChannel *chan) {
1742     struct sockaddr_un remote;
1743     char buffer[512], *ctl_line;
1744     char temp[128];
1745     int sock, clientsock, n, done;
1746     unsigned int t;
1747
1748     sock = g_io_channel_unix_get_fd(chan);
1749
1750     memset (buffer, 0, sizeof (buffer));
1751
1752     t          = sizeof (remote);
1753     clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1754
1755     done = 0;
1756     do {
1757         memset (temp, 0, sizeof (temp));
1758         n = recv (clientsock, temp, 128, 0);
1759         if (n == 0) {
1760             buffer[strlen (buffer)] = '\0';
1761             done = 1;
1762         }
1763         if (!done)
1764             strcat (buffer, temp);
1765     } while (!done);
1766
1767     if (strcmp (buffer, "\n") < 0) {
1768         buffer[strlen (buffer) - 1] = '\0';
1769     } else {
1770         buffer[strlen (buffer)] = '\0';
1771     }
1772     close (clientsock);
1773     ctl_line = g_strdup(buffer);
1774     parse_cmd_line (ctl_line);
1775
1776 /*
1777    TODO: we should be able to do it with this.  but glib errors out with "Invalid argument"
1778     GError *error = NULL;
1779     gsize len;
1780     GIOStatus ret;
1781     ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1782     if (ret == G_IO_STATUS_ERROR)
1783         g_error ("Error reading: %s\n", error->message);
1784
1785     printf("Got line %s (%u bytes) \n",ctl_line, len);
1786     if(ctl_line) {
1787        parse_line(ctl_line);
1788 */
1789
1790     g_free(ctl_line);
1791     return TRUE;
1792 }
1793
1794 static gchar*
1795 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1796     if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1797         if (unlink(uzbl.comm.socket_path) == -1)
1798             g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1799         g_free(uzbl.comm.socket_path);
1800         uzbl.comm.socket_path = NULL;
1801     }
1802
1803     if (*dir == ' ') {
1804         g_free(dir);
1805         return NULL;
1806     }
1807
1808     GIOChannel *chan = NULL;
1809     int sock, len;
1810     struct sockaddr_un local;
1811     gchar *path = build_stream_name(SOCKET, dir);
1812
1813     sock = socket (AF_UNIX, SOCK_STREAM, 0);
1814
1815     local.sun_family = AF_UNIX;
1816     strcpy (local.sun_path, path);
1817     unlink (local.sun_path);
1818
1819     len = strlen (local.sun_path) + sizeof (local.sun_family);
1820     if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1821         if (uzbl.state.verbose)
1822             printf ("init_socket: opened in %s\n", path);
1823         listen (sock, 5);
1824
1825         if( (chan = g_io_channel_unix_new(sock)) ) {
1826             g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1827             uzbl.comm.socket_path = path;
1828             return dir;
1829         }
1830     } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1831
1832     /* if we got this far, there was an error; cleanup */
1833     g_free(path);
1834     g_free(dir);
1835     return NULL;
1836 }
1837
1838 /*
1839  NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1840  it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1841 */
1842 // this function may be called very early when the templates are not set (yet), hence the checks
1843 static void
1844 update_title (void) {
1845     Behaviour *b = &uzbl.behave;
1846     gchar *parsed;
1847
1848     if (b->show_status) {
1849         if (b->title_format_short) {
1850             parsed = expand_template(b->title_format_short, FALSE);
1851             gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1852             g_free(parsed);
1853         }
1854         if (b->status_format) {
1855             parsed = expand_template(b->status_format, TRUE);
1856             gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1857             g_free(parsed);
1858         }
1859         if (b->status_background) {
1860             GdkColor color;
1861             gdk_color_parse (b->status_background, &color);
1862             //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)
1863             gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1864         }
1865     } else {
1866         if (b->title_format_long) {
1867             parsed = expand_template(b->title_format_long, FALSE);
1868             gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1869             g_free(parsed);
1870         }
1871     }
1872 }
1873
1874 static gboolean
1875 key_press_cb (GtkWidget* window, GdkEventKey* event)
1876 {
1877     //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1878
1879     (void) window;
1880
1881     if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1882         || 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)
1883         return FALSE;
1884
1885     /* turn off insert mode (if always_insert_mode is not used) */
1886     if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1887         uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1888         update_title();
1889         return TRUE;
1890     }
1891
1892     if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1893         return FALSE;
1894
1895     if (event->keyval == GDK_Escape) {
1896         g_string_truncate(uzbl.state.keycmd, 0);
1897         update_title();
1898         dehilight(uzbl.gui.web_view, NULL);
1899         return TRUE;
1900     }
1901
1902     //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1903     if (event->keyval == GDK_Insert) {
1904         gchar * str;
1905         if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1906             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1907         } else {
1908             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1909         }
1910         if (str) {
1911             g_string_append (uzbl.state.keycmd, str);
1912             update_title ();
1913             g_free (str);
1914         }
1915         return TRUE;
1916     }
1917
1918     if (event->keyval == GDK_BackSpace)
1919         keycmd_bs(NULL, NULL);
1920
1921     gboolean key_ret = FALSE;
1922     if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1923         key_ret = TRUE;
1924     if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1925
1926     run_keycmd(key_ret);
1927     update_title();
1928     if (key_ret) return (!uzbl.behave.insert_mode);
1929     return TRUE;
1930 }
1931
1932 static void
1933 run_keycmd(const gboolean key_ret) {
1934     /* run the keycmd immediately if it isn't incremental and doesn't take args */
1935     Action *act;
1936     if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1937         g_string_truncate(uzbl.state.keycmd, 0);
1938         parse_command(act->name, act->param);
1939         return;
1940     }
1941
1942     /* try if it's an incremental keycmd or one that takes args, and run it */
1943     GString* short_keys = g_string_new ("");
1944     GString* short_keys_inc = g_string_new ("");
1945     guint i;
1946     for (i=0; i<(uzbl.state.keycmd->len); i++) {
1947         g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1948         g_string_assign(short_keys_inc, short_keys->str);
1949         g_string_append_c(short_keys, '_');
1950         g_string_append_c(short_keys_inc, '*');
1951
1952         if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1953             /* run normal cmds only if return was pressed */
1954             exec_paramcmd(act, i);
1955             g_string_truncate(uzbl.state.keycmd, 0);
1956             break;
1957         } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1958             if (key_ret)  /* just quit the incremental command on return */
1959                 g_string_truncate(uzbl.state.keycmd, 0);
1960             else exec_paramcmd(act, i); /* otherwise execute the incremental */
1961             break;
1962         }
1963         
1964         g_string_truncate(short_keys, short_keys->len - 1);
1965     }
1966     g_string_free (short_keys, TRUE);
1967     g_string_free (short_keys_inc, TRUE);
1968 }
1969
1970 static void
1971 exec_paramcmd(const Action *act, const guint i) {
1972     GString *parampart = g_string_new (uzbl.state.keycmd->str);
1973     GString *actionname = g_string_new ("");
1974     GString *actionparam = g_string_new ("");
1975     g_string_erase (parampart, 0, i+1);
1976     if (act->name)
1977         g_string_printf (actionname, act->name, parampart->str);
1978     if (act->param)
1979         g_string_printf (actionparam, act->param, parampart->str);
1980     parse_command(actionname->str, actionparam->str);
1981     g_string_free(actionname, TRUE);
1982     g_string_free(actionparam, TRUE);
1983     g_string_free(parampart, TRUE);
1984 }
1985
1986
1987 static GtkWidget*
1988 create_browser () {
1989     GUI *g = &uzbl.gui;
1990
1991     GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1992     //main_window_ref = g_object_ref(scrolled_window);
1993     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
1994
1995     g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1996     gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1997
1998     g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1999     g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2000     g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2001     g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2002     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2003     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2004     g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2005     g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2006     g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2007     g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2008     g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2009
2010     return scrolled_window;
2011 }
2012
2013 static GtkWidget*
2014 create_mainbar () {
2015     GUI *g = &uzbl.gui;
2016
2017     g->mainbar = gtk_hbox_new (FALSE, 0);
2018
2019     /* keep a reference to the bar so we can re-pack it at runtime*/
2020     //sbar_ref = g_object_ref(g->mainbar);
2021
2022     g->mainbar_label = gtk_label_new ("");
2023     gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2024     gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2025     gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2026     gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2027     gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2028     return g->mainbar;
2029 }
2030
2031 static
2032 GtkWidget* create_window () {
2033     GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2034     gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2035     gtk_widget_set_name (window, "Uzbl browser");
2036     g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2037     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2038
2039     return window;
2040 }
2041
2042 static gchar**
2043 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2044     /*
2045       If actname is one that calls an external command, this function will inject
2046       newargs in front of the user-provided args in that command line.  They will
2047       come become after the body of the script (in sh) or after the name of
2048       the command to execute (in spawn).
2049       i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2050       span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2051
2052       The return value consist of two strings: the action (sh, ...) and its args.
2053
2054       If act is not one that calls an external command, then the given action merely
2055       gets duplicated.
2056     */
2057     GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2058     gchar *actdup = g_strdup(actname);
2059     g_array_append_val(rets, actdup);
2060
2061     if ((g_strcmp0(actname, "spawn") == 0) ||
2062         (g_strcmp0(actname, "sh") == 0) ||
2063         (g_strcmp0(actname, "sync_spawn") == 0) ||
2064         (g_strcmp0(actname, "sync_sh") == 0)) {
2065         guint i;
2066         GString *a = g_string_new("");
2067         gchar **spawnparts = split_quoted(origargs, FALSE);
2068         g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2069         if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2070
2071         for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2072             if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2073
2074         g_array_append_val(rets, a->str);
2075         g_string_free(a, FALSE);
2076         g_strfreev(spawnparts);
2077     } else {
2078         gchar *origdup = g_strdup(origargs);
2079         g_array_append_val(rets, origdup);
2080     }
2081     return (gchar**)g_array_free(rets, FALSE);
2082 }
2083
2084 static void
2085 run_handler (const gchar *act, const gchar *args) {
2086     /* Consider this code a temporary hack to make the handlers usable.
2087        In practice, all this splicing, injection, and reconstruction is
2088        inefficient, annoying and hard to manage.  Potential pitfalls arise
2089        when the handler specific args 1) are not quoted  (the handler
2090        callbacks should take care of this)  2) are quoted but interfere
2091        with the users' own quotation.  A more ideal solution is
2092        to refactor parse_command so that it doesn't just take a string
2093        and execute it; rather than that, we should have a function which
2094        returns the argument vector parsed from the string.  This vector
2095        could be modified (e.g. insert additional args into it) before
2096        passing it to the next function that actually executes it.  Though
2097        it still isn't perfect for chain actions..  will reconsider & re-
2098        factor when I have the time. -duc */
2099
2100     char **parts = g_strsplit(act, " ", 2);
2101     if (!parts) return;
2102     if (g_strcmp0(parts[0], "chain") == 0) {
2103         GString *newargs = g_string_new("");
2104         gchar **chainparts = split_quoted(parts[1], FALSE);
2105         
2106         /* for every argument in the chain, inject the handler args
2107            and make sure the new parts are wrapped in quotes */
2108         gchar **cp = chainparts;
2109         gchar quot = '\'';
2110         gchar *quotless = NULL;
2111         gchar **spliced_quotless = NULL; // sigh -_-;
2112         gchar **inpart = NULL;
2113         
2114         while (*cp) {
2115             if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2116                 quot = **cp;
2117                 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2118             } else quotless = g_strdup(*cp);
2119
2120             spliced_quotless = g_strsplit(quotless, " ", 2);
2121             inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2122             g_strfreev(spliced_quotless);
2123             
2124             g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2125             g_free(quotless);
2126             g_strfreev(inpart);
2127             cp++;
2128         }
2129
2130         parse_command(parts[0], &(newargs->str[1]));
2131         g_string_free(newargs, TRUE);
2132         g_strfreev(chainparts);
2133         
2134     } else {
2135         gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2136         parse_command(inparts[0], inparts[1]);
2137         g_free(inparts[0]);
2138         g_free(inparts[1]);
2139     }
2140     g_strfreev(parts);
2141 }
2142
2143 static void
2144 add_binding (const gchar *key, const gchar *act) {
2145     char **parts = g_strsplit(act, " ", 2);
2146     Action *action;
2147
2148     if (!parts)
2149         return;
2150
2151     //Debug:
2152     if (uzbl.state.verbose)
2153         printf ("Binding %-10s : %s\n", key, act);
2154     action = new_action(parts[0], parts[1]);
2155
2156     if (g_hash_table_remove (uzbl.bindings, key))
2157         g_warning ("Overwriting existing binding for \"%s\"", key);
2158     g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2159     g_strfreev(parts);
2160 }
2161
2162 static gchar*
2163 get_xdg_var (XDG_Var xdg) {
2164     const gchar* actual_value = getenv (xdg.environmental);
2165     const gchar* home         = getenv ("HOME");
2166
2167     gchar* return_value = str_replace ("~", home, actual_value);
2168
2169     if (! actual_value || strcmp (actual_value, "") == 0) {
2170         if (xdg.default_value) {
2171             return_value = str_replace ("~", home, xdg.default_value);
2172         } else {
2173             return_value = NULL;
2174         }
2175     }
2176     return return_value;
2177 }
2178
2179 static gchar*
2180 find_xdg_file (int xdg_type, char* filename) {
2181     /* xdg_type = 0 => config
2182        xdg_type = 1 => data
2183        xdg_type = 2 => cache*/
2184
2185     gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2186     gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2187     g_free (xdgv);
2188
2189     gchar* temporary_string;
2190     char*  saveptr;
2191     char*  buf;
2192
2193     if (! file_exists (temporary_file) && xdg_type != 2) {
2194         buf = get_xdg_var (XDG[3 + xdg_type]);
2195         temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2196         g_free(buf);
2197
2198         while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2199             g_free (temporary_file);
2200             temporary_file = g_strconcat (temporary_string, filename, NULL);
2201         }
2202     }
2203     
2204     //g_free (temporary_string); - segfaults.
2205
2206     if (file_exists (temporary_file)) {
2207         return temporary_file;
2208     } else {
2209         return NULL;
2210     }
2211 }
2212 static void
2213 settings_init () {
2214     State *s = &uzbl.state;
2215     Network *n = &uzbl.net;
2216     int i;
2217     for (i = 0; default_config[i].command != NULL; i++) {
2218         parse_cmd_line(default_config[i].command);
2219     }
2220
2221     if (!s->config_file) {
2222         s->config_file = find_xdg_file (0, "/uzbl/config");
2223     }
2224
2225     if (s->config_file) {
2226         GArray* lines = read_file_by_line (s->config_file);
2227         int i = 0;
2228         gchar* line;
2229
2230         while ((line = g_array_index(lines, gchar*, i))) {
2231             parse_cmd_line (line);
2232             i ++;
2233             g_free (line);
2234         }
2235         g_array_free (lines, TRUE);
2236     } else {
2237         if (uzbl.state.verbose)
2238             printf ("No configuration file loaded.\n");
2239     }
2240
2241     g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
2242 }
2243
2244 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2245     (void) session;
2246     (void) user_data;
2247     if (!uzbl.behave.cookie_handler)
2248          return;
2249
2250     soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2251     GString *s = g_string_new ("");
2252     SoupURI * soup_uri = soup_message_get_uri(msg);
2253     g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2254     run_handler(uzbl.behave.cookie_handler, s->str);
2255
2256     if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2257         char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2258         if ( p != NULL ) *p = '\0';
2259         soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2260     }
2261     if (uzbl.comm.sync_stdout)
2262         uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2263         
2264     g_string_free(s, TRUE);
2265 }
2266
2267 static void
2268 save_cookies (SoupMessage *msg, gpointer user_data){
2269     (void) user_data;
2270     GSList *ck;
2271     char *cookie;
2272     for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2273         cookie = soup_cookie_to_set_cookie_header(ck->data);
2274         SoupURI * soup_uri = soup_message_get_uri(msg);
2275         GString *s = g_string_new ("");
2276         g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2277         run_handler(uzbl.behave.cookie_handler, s->str);
2278         g_free (cookie);
2279         g_string_free(s, TRUE);
2280     }
2281     g_slist_free(ck);
2282 }
2283
2284 /* --- WEBINSPECTOR --- */
2285 static void
2286 hide_window_cb(GtkWidget *widget, gpointer data) {
2287     (void) data;
2288
2289     gtk_widget_hide(widget);
2290 }
2291
2292 static WebKitWebView*
2293 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2294     (void) data;
2295     (void) page;
2296     (void) web_inspector;
2297     GtkWidget* scrolled_window;
2298     GtkWidget* new_web_view;
2299     GUI *g = &uzbl.gui;
2300
2301     g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2302     g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2303             G_CALLBACK(hide_window_cb), NULL);
2304
2305     gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2306     gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2307     gtk_widget_show(g->inspector_window);
2308
2309     scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2310     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2311             GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2312     gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2313     gtk_widget_show(scrolled_window);
2314
2315     new_web_view = webkit_web_view_new();
2316     gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2317
2318     return WEBKIT_WEB_VIEW(new_web_view);
2319 }
2320
2321 static gboolean
2322 inspector_show_window_cb (WebKitWebInspector* inspector){
2323     (void) inspector;
2324     gtk_widget_show(uzbl.gui.inspector_window);
2325     return TRUE;
2326 }
2327
2328 /* TODO: Add variables and code to make use of these functions */
2329 static gboolean
2330 inspector_close_window_cb (WebKitWebInspector* inspector){
2331     (void) inspector;
2332     return TRUE;
2333 }
2334
2335 static gboolean
2336 inspector_attach_window_cb (WebKitWebInspector* inspector){
2337     (void) inspector;
2338     return FALSE;
2339 }
2340
2341 static gboolean
2342 inspector_dettach_window_cb (WebKitWebInspector* inspector){
2343     (void) inspector;
2344     return FALSE;
2345 }
2346
2347 static gboolean
2348 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2349     (void) inspector;
2350     return FALSE;
2351 }
2352
2353 static gboolean
2354 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2355     (void) inspector;
2356     return FALSE;
2357 }
2358
2359 static void
2360 set_up_inspector() {
2361     GUI *g = &uzbl.gui;
2362     WebKitWebSettings *settings = view_settings();
2363     g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2364
2365     uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2366     g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2367     g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2368     g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2369     g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2370     g_signal_connect (G_OBJECT (g->inspector), "dettach-window", G_CALLBACK (inspector_dettach_window_cb), NULL);
2371     g_signal_connect (G_OBJECT (g->inspector), "destroy", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2372
2373     g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2374 }
2375
2376 static void
2377 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2378     (void) ud;
2379     uzbl_cmdprop *c = v;
2380
2381     if(!c->dump)
2382         return;
2383
2384     if(c->type == TYPE_STR)
2385         printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2386     else if(c->type == TYPE_INT)
2387         printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2388 }
2389
2390 static void
2391 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2392     (void) ud;
2393     Action *a = v;
2394
2395     printf("bind %s = %s %s\n", (char *)k ,
2396             (char *)a->name, a->param?(char *)a->param:"");
2397 }
2398
2399 static void
2400 dump_config() {
2401     g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2402     g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2403 }
2404
2405 /** -- MAIN -- **/
2406 int
2407 main (int argc, char* argv[]) {
2408     gtk_init (&argc, &argv);
2409     if (!g_thread_supported ())
2410         g_thread_init (NULL);
2411     uzbl.state.executable_path = g_strdup(argv[0]);
2412     uzbl.state.selected_url = NULL;
2413     uzbl.state.searchtx = NULL;
2414
2415     GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2416     g_option_context_add_main_entries (context, entries, NULL);
2417     g_option_context_add_group (context, gtk_get_option_group (TRUE));
2418     g_option_context_parse (context, &argc, &argv, NULL);
2419     g_option_context_free(context);
2420     
2421     gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2422     gboolean verbose_override = uzbl.state.verbose;
2423
2424     /* initialize hash table */
2425     uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2426
2427     uzbl.net.soup_session = webkit_get_default_session();
2428     uzbl.state.keycmd = g_string_new("");
2429
2430     if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2431         fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2432     if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2433         fprintf(stderr, "uzbl: error hooking SIGINT\n");
2434     if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2435         fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2436
2437
2438     if(uname(&uzbl.state.unameinfo) == -1)
2439         g_printerr("Can't retrieve unameinfo.  Your useragent might appear wrong.\n");
2440
2441     uzbl.gui.sbar.progress_s = g_strdup("=");
2442     uzbl.gui.sbar.progress_u = g_strdup("ยท");
2443     uzbl.gui.sbar.progress_w = 10;
2444
2445     /* HTML mode defaults*/
2446     uzbl.behave.html_buffer = g_string_new("");
2447     uzbl.behave.html_endmarker = g_strdup(".");
2448     uzbl.behave.html_timeout = 60;
2449     uzbl.behave.base_url = g_strdup("http://invalid");
2450
2451     /* default mode indicators */
2452     uzbl.behave.insert_indicator = g_strdup("I");
2453     uzbl.behave.cmd_indicator    = g_strdup("C");
2454
2455     setup_scanner();
2456     commands_hash ();
2457     make_var_to_name_hash();
2458
2459     uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2460
2461     uzbl.gui.scrolled_win = create_browser();
2462     create_mainbar();
2463
2464     /* initial packing */
2465     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2466     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2467
2468     uzbl.gui.main_window = create_window ();
2469     gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2470
2471
2472     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2473     gtk_widget_show_all (uzbl.gui.main_window);
2474     uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2475
2476     if (uzbl.state.verbose) {
2477         printf("Uzbl start location: %s\n", argv[0]);
2478         printf("window_id %i\n",(int) uzbl.xwin);
2479         printf("pid %i\n", getpid ());
2480         printf("name: %s\n", uzbl.state.instance_name);
2481     }
2482
2483     uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2484     uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2485     uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2486     uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2487     gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2488
2489     settings_init ();
2490
2491     if (!uzbl.behave.show_status)
2492         gtk_widget_hide(uzbl.gui.mainbar);
2493     else
2494         update_title();
2495
2496     /* WebInspector */
2497     set_up_inspector();
2498
2499     create_stdin();
2500
2501     if (verbose_override > uzbl.state.verbose)
2502         uzbl.state.verbose = verbose_override;
2503     
2504     if (uri_override) {
2505         set_var_value("uri", uri_override);
2506         g_free(uri_override);
2507     } else if (uzbl.state.uri)
2508         cmd_load_uri(uzbl.gui.web_view, NULL);
2509
2510     gtk_main ();
2511     clean_up();
2512
2513     return EXIT_SUCCESS;
2514 }
2515
2516 /* vi: set et ts=4: */