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