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