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