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