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