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