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