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