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