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