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