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