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