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