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