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