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