Send Home and End straight to WebKit too.
[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_Home      ||
2191         event->keyval == GDK_End       ||
2192         event->keyval == GDK_Up        ||
2193         event->keyval == GDK_Down      ||
2194         event->keyval == GDK_Left      ||
2195         event->keyval == GDK_Right     ||
2196         event->keyval == GDK_Shift_L   ||
2197         event->keyval == GDK_Shift_R)
2198         return FALSE;
2199
2200     /* turn off insert mode (if always_insert_mode is not used) */
2201     if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2202         set_insert_mode(uzbl.behave.always_insert_mode);
2203         update_title();
2204         return TRUE;
2205     }
2206
2207     if (uzbl.behave.insert_mode &&
2208         ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
2209           (!uzbl.behave.modmask)
2210         )
2211        )
2212         return FALSE;
2213
2214     if (event->keyval == GDK_Escape) {
2215         clear_keycmd();
2216         update_title();
2217         dehilight(uzbl.gui.web_view, NULL, NULL);
2218         return TRUE;
2219     }
2220
2221     //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2222     if (event->keyval == GDK_Insert) {
2223         gchar * str;
2224         if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2225             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2226         } else {
2227             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2228         }
2229         if (str) {
2230             GString* keycmd = g_string_new(uzbl.state.keycmd);
2231             g_string_append (keycmd, str);
2232             uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2233             update_title ();
2234             g_free (str);
2235         }
2236         return TRUE;
2237     }
2238
2239     if (event->keyval == GDK_BackSpace)
2240         keycmd_bs(NULL, NULL, NULL);
2241
2242     gboolean key_ret = FALSE;
2243     if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2244         key_ret = TRUE;
2245     if (!key_ret) {
2246         GString* keycmd = g_string_new(uzbl.state.keycmd);
2247         g_string_append(keycmd, event->string);
2248         uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2249     }
2250
2251     run_keycmd(key_ret);
2252     update_title();
2253     if (key_ret) return (!uzbl.behave.insert_mode);
2254     return TRUE;
2255 }
2256
2257 void
2258 run_keycmd(const gboolean key_ret) {
2259     /* run the keycmd immediately if it isn't incremental and doesn't take args */
2260     Action *act;
2261     if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2262         clear_keycmd();
2263         parse_command(act->name, act->param, NULL);
2264         return;
2265     }
2266
2267     /* try if it's an incremental keycmd or one that takes args, and run it */
2268     GString* short_keys = g_string_new ("");
2269     GString* short_keys_inc = g_string_new ("");
2270     guint i;
2271     guint len = strlen(uzbl.state.keycmd);
2272     for (i=0; i<len; i++) {
2273         g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2274         g_string_assign(short_keys_inc, short_keys->str);
2275         g_string_append_c(short_keys, '_');
2276         g_string_append_c(short_keys_inc, '*');
2277
2278         if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2279             /* run normal cmds only if return was pressed */
2280             exec_paramcmd(act, i);
2281             clear_keycmd();
2282             break;
2283         } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2284             if (key_ret)  /* just quit the incremental command on return */
2285                 clear_keycmd();
2286             else exec_paramcmd(act, i); /* otherwise execute the incremental */
2287             break;
2288         }
2289
2290         g_string_truncate(short_keys, short_keys->len - 1);
2291     }
2292     g_string_free (short_keys, TRUE);
2293     g_string_free (short_keys_inc, TRUE);
2294 }
2295
2296 void
2297 exec_paramcmd(const Action *act, const guint i) {
2298     GString *parampart = g_string_new (uzbl.state.keycmd);
2299     GString *actionname = g_string_new ("");
2300     GString *actionparam = g_string_new ("");
2301     g_string_erase (parampart, 0, i+1);
2302     if (act->name)
2303         g_string_printf (actionname, act->name, parampart->str);
2304     if (act->param)
2305         g_string_printf (actionparam, act->param, parampart->str);
2306     parse_command(actionname->str, actionparam->str, NULL);
2307     g_string_free(actionname, TRUE);
2308     g_string_free(actionparam, TRUE);
2309     g_string_free(parampart, TRUE);
2310 }
2311
2312
2313 void
2314 create_browser () {
2315     GUI *g = &uzbl.gui;
2316
2317     g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2318
2319     g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2320     g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2321     g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2322     g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2323     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2324     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2325     g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2326     g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2327     g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2328     g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2329     g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2330 }
2331
2332 GtkWidget*
2333 create_mainbar () {
2334     GUI *g = &uzbl.gui;
2335
2336     g->mainbar = gtk_hbox_new (FALSE, 0);
2337
2338     /* keep a reference to the bar so we can re-pack it at runtime*/
2339     //sbar_ref = g_object_ref(g->mainbar);
2340
2341     g->mainbar_label = gtk_label_new ("");
2342     gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2343     gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2344     gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2345     gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2346     gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2347     g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2348     return g->mainbar;
2349 }
2350
2351 GtkWidget*
2352 create_window () {
2353     GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2354     gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2355     gtk_widget_set_name (window, "Uzbl browser");
2356     g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2357     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2358     g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2359
2360     return window;
2361 }
2362
2363 GtkPlug*
2364 create_plug () {
2365     GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2366     g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2367     g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2368
2369     return plug;
2370 }
2371
2372
2373 gchar**
2374 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2375     /*
2376       If actname is one that calls an external command, this function will inject
2377       newargs in front of the user-provided args in that command line.  They will
2378       come become after the body of the script (in sh) or after the name of
2379       the command to execute (in spawn).
2380       i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2381       spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2382
2383       The return value consist of two strings: the action (sh, ...) and its args.
2384
2385       If act is not one that calls an external command, then the given action merely
2386       gets duplicated.
2387     */
2388     GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2389     /* Arrr! Here be memory leaks */
2390     gchar *actdup = g_strdup(actname);
2391     g_array_append_val(rets, actdup);
2392
2393     if ((g_strcmp0(actname, "spawn") == 0) ||
2394         (g_strcmp0(actname, "sh") == 0) ||
2395         (g_strcmp0(actname, "sync_spawn") == 0) ||
2396         (g_strcmp0(actname, "sync_sh") == 0) ||
2397         (g_strcmp0(actname, "talk_to_socket") == 0)) {
2398         guint i;
2399         GString *a = g_string_new("");
2400         gchar **spawnparts = split_quoted(origargs, FALSE);
2401         g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2402         if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2403
2404         for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2405             if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2406
2407         g_array_append_val(rets, a->str);
2408         g_string_free(a, FALSE);
2409         g_strfreev(spawnparts);
2410     } else {
2411         gchar *origdup = g_strdup(origargs);
2412         g_array_append_val(rets, origdup);
2413     }
2414     return (gchar**)g_array_free(rets, FALSE);
2415 }
2416
2417 void
2418 run_handler (const gchar *act, const gchar *args) {
2419     /* Consider this code a temporary hack to make the handlers usable.
2420        In practice, all this splicing, injection, and reconstruction is
2421        inefficient, annoying and hard to manage.  Potential pitfalls arise
2422        when the handler specific args 1) are not quoted  (the handler
2423        callbacks should take care of this)  2) are quoted but interfere
2424        with the users' own quotation.  A more ideal solution is
2425        to refactor parse_command so that it doesn't just take a string
2426        and execute it; rather than that, we should have a function which
2427        returns the argument vector parsed from the string.  This vector
2428        could be modified (e.g. insert additional args into it) before
2429        passing it to the next function that actually executes it.  Though
2430        it still isn't perfect for chain actions..  will reconsider & re-
2431        factor when I have the time. -duc */
2432
2433     char **parts = g_strsplit(act, " ", 2);
2434     if (!parts) return;
2435     if (g_strcmp0(parts[0], "chain") == 0) {
2436         GString *newargs = g_string_new("");
2437         gchar **chainparts = split_quoted(parts[1], FALSE);
2438
2439         /* for every argument in the chain, inject the handler args
2440            and make sure the new parts are wrapped in quotes */
2441         gchar **cp = chainparts;
2442         gchar quot = '\'';
2443         gchar *quotless = NULL;
2444         gchar **spliced_quotless = NULL; // sigh -_-;
2445         gchar **inpart = NULL;
2446
2447         while (*cp) {
2448             if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2449                 quot = **cp;
2450                 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2451             } else quotless = g_strdup(*cp);
2452
2453             spliced_quotless = g_strsplit(quotless, " ", 2);
2454             inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2455             g_strfreev(spliced_quotless);
2456
2457             g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2458             g_free(quotless);
2459             g_strfreev(inpart);
2460             cp++;
2461         }
2462
2463         parse_command(parts[0], &(newargs->str[1]), NULL);
2464         g_string_free(newargs, TRUE);
2465         g_strfreev(chainparts);
2466
2467     } else {
2468         gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2469         parse_command(inparts[0], inparts[1], NULL);
2470         g_free(inparts[0]);
2471         g_free(inparts[1]);
2472     }
2473     g_strfreev(parts);
2474 }
2475
2476 void
2477 add_binding (const gchar *key, const gchar *act) {
2478     char **parts = g_strsplit(act, " ", 2);
2479     Action *action;
2480
2481     if (!parts)
2482         return;
2483
2484     //Debug:
2485     if (uzbl.state.verbose)
2486         printf ("Binding %-10s : %s\n", key, act);
2487     action = new_action(parts[0], parts[1]);
2488
2489     if (g_hash_table_remove (uzbl.bindings, key))
2490         g_warning ("Overwriting existing binding for \"%s\"", key);
2491     g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2492     g_strfreev(parts);
2493 }
2494
2495 /*@null@*/ gchar*
2496 get_xdg_var (XDG_Var xdg) {
2497     const gchar* actual_value = getenv (xdg.environmental);
2498     const gchar* home         = getenv ("HOME");
2499     gchar* return_value;
2500
2501     if (! actual_value || strcmp (actual_value, "") == 0) {
2502         if (xdg.default_value) {
2503             return_value = str_replace ("~", home, xdg.default_value);
2504         } else {
2505             return_value = NULL;
2506         }
2507     } else {
2508         return_value = str_replace("~", home, actual_value);
2509     }
2510
2511     return return_value;
2512 }
2513
2514 /*@null@*/ gchar*
2515 find_xdg_file (int xdg_type, const char* filename) {
2516     /* xdg_type = 0 => config
2517        xdg_type = 1 => data
2518        xdg_type = 2 => cache*/
2519
2520     gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2521     gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2522     g_free (xdgv);
2523
2524     gchar* temporary_string;
2525     char*  saveptr;
2526     char*  buf;
2527
2528     if (! file_exists (temporary_file) && xdg_type != 2) {
2529         buf = get_xdg_var (XDG[3 + xdg_type]);
2530         temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2531         g_free(buf);
2532
2533         while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2534             g_free (temporary_file);
2535             temporary_file = g_strconcat (temporary_string, filename, NULL);
2536         }
2537     }
2538
2539     //g_free (temporary_string); - segfaults.
2540
2541     if (file_exists (temporary_file)) {
2542         return temporary_file;
2543     } else {
2544         g_free(temporary_file);
2545         return NULL;
2546     }
2547 }
2548 void
2549 settings_init () {
2550     State *s = &uzbl.state;
2551     Network *n = &uzbl.net;
2552     int i;
2553     for (i = 0; default_config[i].command != NULL; i++) {
2554         parse_cmd_line(default_config[i].command, NULL);
2555     }
2556
2557     if (g_strcmp0(s->config_file, "-") == 0) {
2558         s->config_file = NULL;
2559         create_stdin();
2560     }
2561
2562     else if (!s->config_file) {
2563         s->config_file = find_xdg_file (0, "/uzbl/config");
2564     }
2565
2566     if (s->config_file) {
2567         GArray* lines = read_file_by_line (s->config_file);
2568         int i = 0;
2569         gchar* line;
2570
2571         while ((line = g_array_index(lines, gchar*, i))) {
2572             parse_cmd_line (line, NULL);
2573             i ++;
2574             g_free (line);
2575         }
2576         g_array_free (lines, TRUE);
2577     } else {
2578         if (uzbl.state.verbose)
2579             printf ("No configuration file loaded.\n");
2580     }
2581
2582     g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2583 }
2584
2585 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2586     (void) session;
2587     (void) user_data;
2588     if (!uzbl.behave.cookie_handler)
2589          return;
2590
2591     soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2592     GString *s = g_string_new ("");
2593     SoupURI * soup_uri = soup_message_get_uri(msg);
2594     g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2595     run_handler(uzbl.behave.cookie_handler, s->str);
2596
2597     if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2598         char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2599         if ( p != NULL ) *p = '\0';
2600         soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2601     }
2602     if (uzbl.comm.sync_stdout)
2603         uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2604
2605     g_string_free(s, TRUE);
2606 }
2607
2608 void
2609 save_cookies (SoupMessage *msg, gpointer user_data){
2610     (void) user_data;
2611     GSList *ck;
2612     char *cookie;
2613     for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2614         cookie = soup_cookie_to_set_cookie_header(ck->data);
2615         SoupURI * soup_uri = soup_message_get_uri(msg);
2616         GString *s = g_string_new ("");
2617         g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2618         run_handler(uzbl.behave.cookie_handler, s->str);
2619         g_free (cookie);
2620         g_string_free(s, TRUE);
2621     }
2622     g_slist_free(ck);
2623 }
2624
2625 /* --- WEBINSPECTOR --- */
2626 void
2627 hide_window_cb(GtkWidget *widget, gpointer data) {
2628     (void) data;
2629
2630     gtk_widget_hide(widget);
2631 }
2632
2633 WebKitWebView*
2634 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2635     (void) data;
2636     (void) page;
2637     (void) web_inspector;
2638     GtkWidget* scrolled_window;
2639     GtkWidget* new_web_view;
2640     GUI *g = &uzbl.gui;
2641
2642     g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2643     g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2644             G_CALLBACK(hide_window_cb), NULL);
2645
2646     gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2647     gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2648     gtk_widget_show(g->inspector_window);
2649
2650     scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2651     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2652             GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2653     gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2654     gtk_widget_show(scrolled_window);
2655
2656     new_web_view = webkit_web_view_new();
2657     gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2658
2659     return WEBKIT_WEB_VIEW(new_web_view);
2660 }
2661
2662 gboolean
2663 inspector_show_window_cb (WebKitWebInspector* inspector){
2664     (void) inspector;
2665     gtk_widget_show(uzbl.gui.inspector_window);
2666     return TRUE;
2667 }
2668
2669 /* TODO: Add variables and code to make use of these functions */
2670 gboolean
2671 inspector_close_window_cb (WebKitWebInspector* inspector){
2672     (void) inspector;
2673     return TRUE;
2674 }
2675
2676 gboolean
2677 inspector_attach_window_cb (WebKitWebInspector* inspector){
2678     (void) inspector;
2679     return FALSE;
2680 }
2681
2682 gboolean
2683 inspector_detach_window_cb (WebKitWebInspector* inspector){
2684     (void) inspector;
2685     return FALSE;
2686 }
2687
2688 gboolean
2689 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2690     (void) inspector;
2691     return FALSE;
2692 }
2693
2694 gboolean
2695 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2696     (void) inspector;
2697     return FALSE;
2698 }
2699
2700 void
2701 set_up_inspector() {
2702     GUI *g = &uzbl.gui;
2703     WebKitWebSettings *settings = view_settings();
2704     g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2705
2706     uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2707     g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2708     g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2709     g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2710     g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2711     g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2712     g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2713
2714     g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2715 }
2716
2717 void
2718 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2719     (void) ud;
2720     uzbl_cmdprop *c = v;
2721
2722     if(!c->dump)
2723         return;
2724
2725     if(c->type == TYPE_STR)
2726         printf("set %s = %s\n", (char *)k, *c->ptr.s ? *c->ptr.s : " ");
2727     else if(c->type == TYPE_INT)
2728         printf("set %s = %d\n", (char *)k, *c->ptr.i);
2729     else if(c->type == TYPE_FLOAT)
2730         printf("set %s = %f\n", (char *)k, *c->ptr.f);
2731 }
2732
2733 void
2734 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2735     (void) ud;
2736     Action *a = v;
2737
2738     printf("bind %s = %s %s\n", (char *)k ,
2739             (char *)a->name, a->param?(char *)a->param:"");
2740 }
2741
2742 void
2743 dump_config() {
2744     g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2745     g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2746 }
2747
2748 void
2749 retrieve_geometry() {
2750     int w, h, x, y;
2751     GString *buf = g_string_new("");
2752
2753     gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2754     gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2755
2756     g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2757
2758     if(uzbl.gui.geometry)
2759         g_free(uzbl.gui.geometry);
2760     uzbl.gui.geometry = g_string_free(buf, FALSE);
2761 }
2762
2763 /* set up gtk, gobject, variable defaults and other things that tests and other
2764  * external applications need to do anyhow */
2765 void
2766 initialize(int argc, char *argv[]) {
2767     if (!g_thread_supported ())
2768         g_thread_init (NULL);
2769     uzbl.state.executable_path = g_strdup(argv[0]);
2770     uzbl.state.selected_url = NULL;
2771     uzbl.state.searchtx = NULL;
2772
2773     GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2774     g_option_context_add_main_entries (context, entries, NULL);
2775     g_option_context_add_group (context, gtk_get_option_group (TRUE));
2776     g_option_context_parse (context, &argc, &argv, NULL);
2777     g_option_context_free(context);
2778
2779     if (uzbl.behave.print_version) {
2780         printf("Commit: %s\n", COMMIT);
2781         exit(EXIT_SUCCESS);
2782     }
2783
2784     /* initialize hash table */
2785     uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2786
2787     uzbl.net.soup_session = webkit_get_default_session();
2788     uzbl.state.keycmd = g_strdup("");
2789
2790     if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2791         fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2792     if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2793         fprintf(stderr, "uzbl: error hooking SIGINT\n");
2794
2795     uzbl.gui.sbar.progress_s = g_strdup("="); //TODO: move these to config.h
2796     uzbl.gui.sbar.progress_u = g_strdup("ยท");
2797     uzbl.gui.sbar.progress_w = 10;
2798
2799     /* default mode indicators */
2800     uzbl.behave.insert_indicator = g_strdup("I");
2801     uzbl.behave.cmd_indicator    = g_strdup("C");
2802
2803     uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2804     uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2805     uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2806     uzbl.info.arch         = ARCH;
2807     uzbl.info.commit       = COMMIT;
2808
2809     commands_hash ();
2810     make_var_to_name_hash();
2811
2812     create_browser();
2813 }
2814
2815 #ifndef UZBL_LIBRARY
2816 /** -- MAIN -- **/
2817 int
2818 main (int argc, char* argv[]) {
2819     initialize(argc, argv);
2820
2821     gtk_init (&argc, &argv);
2822
2823     uzbl.gui.scrolled_win = gtk_scrolled_window_new (NULL, NULL);
2824     //main_window_ref = g_object_ref(scrolled_window);
2825     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (uzbl.gui.scrolled_win),
2826         GTK_POLICY_NEVER, GTK_POLICY_NEVER); //todo: some sort of display of position/total length. like what emacs does
2827
2828     gtk_container_add (GTK_CONTAINER (uzbl.gui.scrolled_win),
2829         GTK_WIDGET (uzbl.gui.web_view));
2830
2831     uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2832
2833     create_mainbar();
2834
2835     /* initial packing */
2836     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2837     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2838
2839     if (uzbl.state.socket_id) {
2840         uzbl.gui.plug = create_plug ();
2841         gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2842         gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2843     } else {
2844         uzbl.gui.main_window = create_window ();
2845         gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2846         gtk_widget_show_all (uzbl.gui.main_window);
2847         uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2848     }
2849
2850     if(!uzbl.state.instance_name)
2851         uzbl.state.instance_name = itos((int)uzbl.xwin);
2852
2853     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2854
2855     if (uzbl.state.verbose) {
2856         printf("Uzbl start location: %s\n", argv[0]);
2857         if (uzbl.state.socket_id)
2858             printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2859         else
2860             printf("window_id %i\n",(int) uzbl.xwin);
2861         printf("pid %i\n", getpid ());
2862         printf("name: %s\n", uzbl.state.instance_name);
2863     }
2864
2865     uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2866     uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2867     uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2868     uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2869     gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2870
2871     /* Check uzbl is in window mode before getting/setting geometry */
2872     if (uzbl.gui.main_window) {
2873         if(uzbl.gui.geometry)
2874             cmd_set_geometry();
2875         else
2876             retrieve_geometry();
2877     }
2878
2879     gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2880     if (argc > 1 && !uzbl.state.uri)
2881         uri_override = g_strdup(argv[1]);
2882     gboolean verbose_override = uzbl.state.verbose;
2883
2884     settings_init ();
2885     set_insert_mode(FALSE);
2886
2887     if (!uzbl.behave.show_status)
2888         gtk_widget_hide(uzbl.gui.mainbar);
2889     else
2890         update_title();
2891
2892     /* WebInspector */
2893     set_up_inspector();
2894
2895     if (verbose_override > uzbl.state.verbose)
2896         uzbl.state.verbose = verbose_override;
2897
2898     if (uri_override) {
2899         set_var_value("uri", uri_override);
2900         g_free(uri_override);
2901     } else if (uzbl.state.uri)
2902         cmd_load_uri();
2903
2904     gtk_main ();
2905     clean_up();
2906
2907     return EXIT_SUCCESS;
2908 }
2909 #endif
2910
2911 /* vi: set et ts=4: */