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