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