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