Use JavaScriptCore for evaluation, with Uzbl.run()
[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     (void)page;
992     (void)argv;
993     (void)result;
994     g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
995     update_title();
996 }
997
998 static void
999 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1000     (void)page;
1001     (void)argv;
1002     (void)result;
1003     gtk_main_quit ();
1004 }
1005
1006 /* --Statusbar functions-- */
1007 static char*
1008 build_progressbar_ascii(int percent) {
1009    int width=uzbl.gui.sbar.progress_w;
1010    int i;
1011    double l;
1012    GString *bar = g_string_new("");
1013
1014    l = (double)percent*((double)width/100.);
1015    l = (int)(l+.5)>=(int)l ? l+.5 : l;
1016
1017    for(i=0; i<(int)l; i++)
1018        g_string_append(bar, uzbl.gui.sbar.progress_s);
1019
1020    for(; i<width; i++)
1021        g_string_append(bar, uzbl.gui.sbar.progress_u);
1022
1023    return g_string_free(bar, FALSE);
1024 }
1025
1026 static void
1027 setup_scanner() {
1028      const GScannerConfig scan_config = {
1029              (
1030               "\t\r\n"
1031              )            /* cset_skip_characters */,
1032              (
1033               G_CSET_a_2_z
1034               "_#"
1035               G_CSET_A_2_Z
1036              )            /* cset_identifier_first */,
1037              (
1038               G_CSET_a_2_z
1039               "_0123456789"
1040               G_CSET_A_2_Z
1041               G_CSET_LATINS
1042               G_CSET_LATINC
1043              )            /* cset_identifier_nth */,
1044              ( "" )    /* cpair_comment_single */,
1045
1046              TRUE         /* case_sensitive */,
1047
1048              FALSE        /* skip_comment_multi */,
1049              FALSE        /* skip_comment_single */,
1050              FALSE        /* scan_comment_multi */,
1051              TRUE         /* scan_identifier */,
1052              TRUE         /* scan_identifier_1char */,
1053              FALSE        /* scan_identifier_NULL */,
1054              TRUE         /* scan_symbols */,
1055              FALSE        /* scan_binary */,
1056              FALSE        /* scan_octal */,
1057              FALSE        /* scan_float */,
1058              FALSE        /* scan_hex */,
1059              FALSE        /* scan_hex_dollar */,
1060              FALSE        /* scan_string_sq */,
1061              FALSE        /* scan_string_dq */,
1062              TRUE         /* numbers_2_int */,
1063              FALSE        /* int_2_float */,
1064              FALSE        /* identifier_2_string */,
1065              FALSE        /* char_2_token */,
1066              FALSE        /* symbol_2_token */,
1067              TRUE         /* scope_0_fallback */,
1068              FALSE,
1069              TRUE
1070      };
1071
1072      uzbl.scan = g_scanner_new(&scan_config);
1073      while(symp->symbol_name) {
1074          g_scanner_scope_add_symbol(uzbl.scan, 0,
1075                          symp->symbol_name,
1076                          GINT_TO_POINTER(symp->symbol_token));
1077          symp++;
1078      }
1079 }
1080
1081 static gchar *
1082 expand_template(const char *template, gboolean escape_markup) {
1083      if(!template) return NULL;
1084
1085      GTokenType token = G_TOKEN_NONE;
1086      GString *ret = g_string_new("");
1087      char *buf=NULL;
1088      int sym;
1089
1090      g_scanner_input_text(uzbl.scan, template, strlen(template));
1091      while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1092          token = g_scanner_get_next_token(uzbl.scan);
1093
1094          if(token == G_TOKEN_SYMBOL) {
1095              sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1096              switch(sym) {
1097                  case SYM_URI:
1098                      if(escape_markup) {
1099                          buf = uzbl.state.uri?
1100                              g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
1101                          g_string_append(ret, buf);
1102                          g_free(buf);
1103                      }
1104                      else
1105                          g_string_append(ret, uzbl.state.uri?
1106                                  uzbl.state.uri:g_strdup(""));
1107                      break;
1108                  case SYM_LOADPRGS:
1109                      buf = itos(uzbl.gui.sbar.load_progress);
1110                      g_string_append(ret, buf);
1111                      g_free(buf);
1112                      break;
1113                  case SYM_LOADPRGSBAR:
1114                      buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1115                      g_string_append(ret, buf);
1116                      g_free(buf);
1117                      break;
1118                  case SYM_TITLE:
1119                      if(escape_markup) {
1120                          buf = uzbl.gui.main_title?
1121                              g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
1122                          g_string_append(ret, buf);
1123                          g_free(buf);
1124                      }
1125                      else
1126                          g_string_append(ret, uzbl.gui.main_title?
1127                                  uzbl.gui.main_title:g_strdup(""));
1128                      break;
1129                  case SYM_SELECTED_URI:
1130                      if(escape_markup) {
1131                          buf = uzbl.state.selected_url?
1132                              g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
1133                          g_string_append(ret, buf);
1134                          g_free(buf);
1135                      }
1136                      else
1137                          g_string_append(ret, uzbl.state.selected_url?
1138                                  uzbl.state.selected_url:g_strdup(""));
1139                      break;
1140                  case SYM_NAME:
1141                      buf = itos(uzbl.xwin);
1142                      g_string_append(ret,
1143                              uzbl.state.instance_name?uzbl.state.instance_name:buf);
1144                      g_free(buf);
1145                      break;
1146                  case SYM_KEYCMD:
1147                      if(escape_markup) {
1148                          buf = uzbl.state.keycmd->str?
1149                              g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1150                          g_string_append(ret, buf);
1151                          g_free(buf);
1152                      }
1153                      else
1154                          g_string_append(ret, uzbl.state.keycmd->str?
1155                                  uzbl.state.keycmd->str:g_strdup(""));
1156                      break;
1157                  case SYM_MODE:
1158                      g_string_append(ret,
1159                              uzbl.behave.insert_mode?
1160                              uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1161                      break;
1162                  case SYM_MSG:
1163                      g_string_append(ret,
1164                              uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1165                      break;
1166                      /* useragent syms */
1167                  case SYM_WK_MAJ:
1168                      buf = itos(WEBKIT_MAJOR_VERSION);
1169                      g_string_append(ret, buf);
1170                      g_free(buf);
1171                      break;
1172                  case SYM_WK_MIN:
1173                      buf = itos(WEBKIT_MINOR_VERSION);
1174                      g_string_append(ret, buf);
1175                      g_free(buf);
1176                      break;
1177                  case SYM_WK_MIC:
1178                      buf = itos(WEBKIT_MICRO_VERSION);
1179                      g_string_append(ret, buf);
1180                      g_free(buf);
1181                      break;
1182                  case SYM_SYSNAME:
1183                      g_string_append(ret, uzbl.state.unameinfo.sysname);
1184                      break;
1185                  case SYM_NODENAME:
1186                      g_string_append(ret, uzbl.state.unameinfo.nodename);
1187                      break;
1188                  case SYM_KERNREL:
1189                      g_string_append(ret, uzbl.state.unameinfo.release);
1190                      break;
1191                  case SYM_KERNVER:
1192                      g_string_append(ret, uzbl.state.unameinfo.version);
1193                      break;
1194                  case SYM_ARCHSYS:
1195                      g_string_append(ret, uzbl.state.unameinfo.machine);
1196                      break;
1197                  case SYM_ARCHUZBL:
1198                      g_string_append(ret, ARCH);
1199                      break;
1200 #ifdef _GNU_SOURCE
1201                  case SYM_DOMAINNAME:
1202                      g_string_append(ret, uzbl.state.unameinfo.domainname);
1203                      break;
1204 #endif
1205                  case SYM_COMMIT:
1206                      g_string_append(ret, COMMIT);
1207                      break;
1208                  default:
1209                      break;
1210              }
1211          }
1212          else if(token == G_TOKEN_INT) {
1213              buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1214              g_string_append(ret, buf);
1215              g_free(buf);
1216          }
1217          else if(token == G_TOKEN_IDENTIFIER) {
1218              g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1219          }
1220          else if(token == G_TOKEN_CHAR) {
1221              g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1222          }
1223      }
1224
1225      return g_string_free(ret, FALSE);
1226 }
1227 /* --End Statusbar functions-- */
1228
1229 static void
1230 sharg_append(GArray *a, const gchar *str) {
1231     const gchar *s = (str ? str : "");
1232     g_array_append_val(a, s);
1233 }
1234
1235 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1236 static gboolean
1237 run_command (const gchar *command, const guint npre, const gchar **args,
1238              const gboolean sync, char **output_stdout) {
1239    //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1240     GError *err = NULL;
1241     
1242     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1243     gchar *pid = itos(getpid());
1244     gchar *xwin = itos(uzbl.xwin);
1245     guint i;
1246     sharg_append(a, command);
1247     for (i = 0; i < npre; i++) /* add n args before the default vars */
1248         sharg_append(a, args[i]);
1249     sharg_append(a, uzbl.state.config_file);
1250     sharg_append(a, pid);
1251     sharg_append(a, xwin);
1252     sharg_append(a, uzbl.comm.fifo_path);
1253     sharg_append(a, uzbl.comm.socket_path);
1254     sharg_append(a, uzbl.state.uri);
1255     sharg_append(a, uzbl.gui.main_title);
1256
1257     for (i = npre; i < g_strv_length((gchar**)args); i++)
1258         sharg_append(a, args[i]);
1259     
1260     gboolean result;
1261     if (sync) {
1262         if (*output_stdout) *output_stdout = strfree(*output_stdout);
1263         
1264         result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1265                               NULL, NULL, output_stdout, NULL, NULL, &err);
1266     } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1267                                   NULL, NULL, NULL, &err);
1268
1269     if (uzbl.state.verbose) {
1270         GString *s = g_string_new("spawned:");
1271         for (i = 0; i < (a->len); i++) {
1272             gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1273             g_string_append_printf(s, " %s", qarg);
1274             g_free (qarg);
1275         }
1276         g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1277         printf("%s\n", s->str);
1278         g_string_free(s, TRUE);
1279         if(output_stdout) {
1280             printf("Stdout: %s\n", *output_stdout);
1281         }
1282     }
1283     if (err) {
1284         g_printerr("error on run_command: %s\n", err->message);
1285         g_error_free (err);
1286     }
1287     g_free (pid);
1288     g_free (xwin);
1289     g_array_free (a, TRUE);
1290     return result;
1291 }
1292
1293 static gchar**
1294 split_quoted(const gchar* src, const gboolean unquote) {
1295     /* split on unquoted space, return array of strings;
1296        remove a layer of quotes and backslashes if unquote */
1297     if (!src) return NULL;
1298     
1299     gboolean dq = FALSE;
1300     gboolean sq = FALSE;
1301     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1302     GString *s = g_string_new ("");
1303     const gchar *p;
1304     gchar **ret;
1305     gchar *dup;
1306     for (p = src; *p != '\0'; p++) {
1307         if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1308         else if (*p == '\\') { g_string_append_c(s, *p++);
1309                                g_string_append_c(s, *p); }
1310         else if ((*p == '"') && unquote && !sq) dq = !dq;
1311         else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1312                                      dq = !dq; }
1313         else if ((*p == '\'') && unquote && !dq) sq = !sq;
1314         else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1315                                       sq = ! sq; }
1316         else if ((*p == ' ') && !dq && !sq) {
1317             dup = g_strdup(s->str);
1318             g_array_append_val(a, dup);
1319             g_string_truncate(s, 0);
1320         } else g_string_append_c(s, *p);
1321     }
1322     dup = g_strdup(s->str);
1323     g_array_append_val(a, dup);
1324     ret = (gchar**)a->data;
1325     g_array_free (a, FALSE);
1326     g_string_free (s, TRUE);
1327     return ret;
1328 }
1329
1330 static void
1331 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1332     (void)web_view; (void)result;
1333     //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1334     if (argv_idx(argv, 0))
1335         run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1336 }
1337
1338 static void
1339 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1340     (void)web_view; (void)result;
1341     
1342     if (argv_idx(argv, 0))
1343         run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1344                     TRUE, &uzbl.comm.sync_stdout);
1345 }
1346
1347 static void
1348 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1349     (void)web_view; (void)result;
1350     if (!uzbl.behave.shell_cmd) {
1351         g_printerr ("spawn_sh: shell_cmd is not set!\n");
1352         return;
1353     }
1354     
1355     guint i;
1356     gchar *spacer = g_strdup("");
1357     g_array_insert_val(argv, 1, spacer);
1358     gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1359
1360     for (i = 1; i < g_strv_length(cmd); i++)
1361         g_array_prepend_val(argv, cmd[i]);
1362
1363     if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1364     g_free (spacer);
1365     g_strfreev (cmd);
1366 }
1367
1368 static void
1369 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1370     (void)web_view; (void)result;
1371     if (!uzbl.behave.shell_cmd) {
1372         g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1373         return;
1374     }
1375     
1376     guint i;
1377     gchar *spacer = g_strdup("");
1378     g_array_insert_val(argv, 1, spacer);
1379     gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1380
1381     for (i = 1; i < g_strv_length(cmd); i++)
1382         g_array_prepend_val(argv, cmd[i]);
1383          
1384     if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1385                          TRUE, &uzbl.comm.sync_stdout);
1386     g_free (spacer);
1387     g_strfreev (cmd);
1388 }
1389
1390 static void
1391 parse_command(const char *cmd, const char *param, GString *result) {
1392     CommandInfo *c;
1393
1394     if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1395             guint i;
1396             gchar **par = split_quoted(param, TRUE);
1397             GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1398
1399             if (c->no_split) { /* don't split */
1400                 sharg_append(a, param);
1401             } else if (par) {
1402                 for (i = 0; i < g_strv_length(par); i++)
1403                     sharg_append(a, par[i]);
1404             }
1405
1406             if (result == NULL) {
1407                 GString *result_print = g_string_new("");
1408
1409                 c->function(uzbl.gui.web_view, a, result_print);
1410                 if (result_print->len)
1411                     printf("%*s\n", result_print->len, result_print->str);
1412
1413                 g_string_free(result_print, TRUE);
1414             } else {
1415                 c->function(uzbl.gui.web_view, a, result);
1416             }
1417             g_strfreev (par);
1418             g_array_free (a, TRUE);
1419
1420     } else
1421         g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1422 }
1423
1424 static void
1425 set_proxy_url() {
1426     SoupURI *suri;
1427
1428     if(*uzbl.net.proxy_url == ' '
1429        || uzbl.net.proxy_url == NULL) {
1430         soup_session_remove_feature_by_type(uzbl.net.soup_session,
1431                 (GType) SOUP_SESSION_PROXY_URI);
1432     }
1433     else {
1434         suri = soup_uri_new(uzbl.net.proxy_url);
1435         g_object_set(G_OBJECT(uzbl.net.soup_session),
1436                 SOUP_SESSION_PROXY_URI,
1437                 suri, NULL);
1438         soup_uri_free(suri);
1439     }
1440     return;
1441 }
1442
1443 static void
1444 set_icon() {
1445     if(file_exists(uzbl.gui.icon)) {
1446         if (uzbl.gui.main_window)
1447             gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1448     } else {
1449         g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1450     }
1451     g_free (uzbl.gui.icon);
1452 }
1453
1454 static void
1455 cmd_load_uri() {
1456     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1457     g_array_append_val (a, uzbl.state.uri);
1458     load_uri(uzbl.gui.web_view, a, NULL);
1459     g_array_free (a, TRUE);
1460 }
1461
1462 static void 
1463 cmd_always_insert_mode() {
1464     uzbl.behave.insert_mode =
1465         uzbl.behave.always_insert_mode ?  TRUE : FALSE;
1466     update_title();
1467 }
1468
1469 static void
1470 cmd_max_conns() {
1471     g_object_set(G_OBJECT(uzbl.net.soup_session),
1472             SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1473 }
1474
1475 static void
1476 cmd_max_conns_host() {
1477     g_object_set(G_OBJECT(uzbl.net.soup_session),
1478             SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1479 }
1480
1481 static void
1482 cmd_http_debug() {
1483     soup_session_remove_feature
1484         (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1485     /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1486     /*g_free(uzbl.net.soup_logger);*/
1487
1488     uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1489     soup_session_add_feature(uzbl.net.soup_session,
1490             SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1491 }
1492
1493 static WebKitWebSettings*
1494 view_settings() {
1495     return webkit_web_view_get_settings(uzbl.gui.web_view);
1496 }
1497
1498 static void
1499 cmd_font_size() {
1500     WebKitWebSettings *ws = view_settings();
1501     if (uzbl.behave.font_size > 0) {
1502         g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1503     }
1504     
1505     if (uzbl.behave.monospace_size > 0) {
1506         g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1507                       uzbl.behave.monospace_size, NULL);
1508     } else {
1509         g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1510                       uzbl.behave.font_size, NULL);
1511     }
1512 }
1513
1514 static void
1515 cmd_zoom_level() {
1516     webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1517 }
1518
1519 static void
1520 cmd_disable_plugins() {
1521     g_object_set (G_OBJECT(view_settings()), "enable-plugins", 
1522             !uzbl.behave.disable_plugins, NULL);
1523 }
1524
1525 static void
1526 cmd_disable_scripts() {
1527     g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1528             !uzbl.behave.disable_scripts, NULL);
1529 }
1530
1531 static void
1532 cmd_minimum_font_size() {
1533     g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1534             uzbl.behave.minimum_font_size, NULL);
1535 }
1536 static void
1537 cmd_autoload_img() {
1538     g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1539             uzbl.behave.autoload_img, NULL);
1540 }
1541
1542
1543 static void
1544 cmd_autoshrink_img() {
1545     g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1546             uzbl.behave.autoshrink_img, NULL);
1547 }
1548
1549
1550 static void
1551 cmd_enable_spellcheck() {
1552     g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1553             uzbl.behave.enable_spellcheck, NULL);
1554 }
1555
1556 static void
1557 cmd_enable_private() {
1558     g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1559             uzbl.behave.enable_private, NULL);
1560 }
1561
1562 static void
1563 cmd_print_bg() {
1564     g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1565             uzbl.behave.print_bg, NULL);
1566 }
1567
1568 static void 
1569 cmd_style_uri() {
1570     g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1571             uzbl.behave.style_uri, NULL);
1572 }
1573
1574 static void 
1575 cmd_resizable_txt() {
1576     g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1577             uzbl.behave.resizable_txt, NULL);
1578 }
1579
1580 static void 
1581 cmd_default_encoding() {
1582     g_object_set (G_OBJECT(view_settings()), "default-encoding",
1583             uzbl.behave.default_encoding, NULL);
1584 }
1585
1586 static void 
1587 cmd_enforce_96dpi() {
1588     g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1589             uzbl.behave.enforce_96dpi, NULL);
1590 }
1591
1592 static void 
1593 cmd_caret_browsing() {
1594     g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1595             uzbl.behave.caret_browsing, NULL);
1596 }
1597
1598 static void
1599 cmd_cookie_handler() {
1600     gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1601     /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1602     if ((g_strcmp0(split[0], "sh") == 0) ||
1603         (g_strcmp0(split[0], "spawn") == 0)) {
1604         g_free (uzbl.behave.cookie_handler);
1605         uzbl.behave.cookie_handler =
1606             g_strdup_printf("sync_%s %s", split[0], split[1]);
1607     }
1608     g_strfreev (split);
1609 }
1610
1611 static void
1612 cmd_fifo_dir() {
1613     uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1614 }
1615
1616 static void
1617 cmd_socket_dir() {
1618     uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1619 }
1620
1621 static void
1622 cmd_inject_html() {
1623     if(uzbl.behave.inject_html) {
1624         webkit_web_view_load_html_string (uzbl.gui.web_view,
1625                 uzbl.behave.inject_html, NULL);
1626     }
1627 }
1628
1629 static void
1630 cmd_modkey() {
1631     int i;
1632     char *buf;
1633
1634     buf = g_utf8_strup(uzbl.behave.modkey, -1);
1635     uzbl.behave.modmask = 0;
1636
1637     if(uzbl.behave.modkey) 
1638         g_free(uzbl.behave.modkey);
1639     uzbl.behave.modkey = buf;
1640
1641     for (i = 0; modkeys[i].key != NULL; i++) {
1642         if (g_strrstr(buf, modkeys[i].key))
1643             uzbl.behave.modmask |= modkeys[i].mask;
1644     }
1645 }
1646
1647 static void
1648 cmd_useragent() {
1649     if (*uzbl.net.useragent == ' ') {
1650         g_free (uzbl.net.useragent);
1651         uzbl.net.useragent = NULL;
1652     } else {
1653         gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1654         if (ua)
1655             g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1656         g_free(uzbl.net.useragent);
1657         uzbl.net.useragent = ua;
1658     }
1659 }
1660
1661 static void
1662 move_statusbar() {
1663     gtk_widget_ref(uzbl.gui.scrolled_win);
1664     gtk_widget_ref(uzbl.gui.mainbar);
1665     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1666     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1667
1668     if(uzbl.behave.status_top) {
1669         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1670         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1671     }
1672     else {
1673         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1674         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1675     }
1676     gtk_widget_unref(uzbl.gui.scrolled_win);
1677     gtk_widget_unref(uzbl.gui.mainbar);
1678     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1679     return;
1680 }
1681
1682 static gboolean
1683 set_var_value(gchar *name, gchar *val) {
1684     uzbl_cmdprop *c = NULL;
1685     char *endp = NULL;
1686     char *buf = NULL;
1687
1688     if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1689         /* check for the variable type */
1690         if (c->type == TYPE_STR) {
1691             buf = expand_vars(val);
1692             g_free(*c->ptr);
1693             *c->ptr = buf;
1694         } else if(c->type == TYPE_INT) {
1695             int *ip = (int *)c->ptr;
1696             buf = expand_vars(val);
1697             *ip = (int)strtoul(buf, &endp, 10);
1698             g_free(buf);
1699         } else if (c->type == TYPE_FLOAT) {
1700             float *fp = (float *)c->ptr;
1701             buf = expand_vars(val);
1702             *fp = strtod(buf, &endp);
1703             g_free(buf);
1704         }
1705
1706         /* invoke a command specific function */
1707         if(c->func) c->func();
1708     }
1709     return TRUE;
1710 }
1711
1712 static void
1713 render_html() {
1714     Behaviour *b = &uzbl.behave;
1715
1716     if(b->html_buffer->str) {
1717         webkit_web_view_load_html_string (uzbl.gui.web_view,
1718                 b->html_buffer->str, b->base_url);
1719         g_string_free(b->html_buffer, TRUE);
1720         b->html_buffer = g_string_new("");
1721     }
1722 }
1723
1724 enum {M_CMD, M_HTML};
1725 static void
1726 parse_cmd_line(const char *ctl_line, GString *result) {
1727     Behaviour *b = &uzbl.behave;
1728     size_t len=0;
1729
1730     if(b->mode == M_HTML) {
1731         len = strlen(b->html_endmarker);
1732         /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1733         if(len == strlen(ctl_line)-1 &&
1734            !strncmp(b->html_endmarker, ctl_line, len)) {
1735             set_timeout(0);
1736             set_var_value("mode", "0");
1737             render_html();
1738             return;
1739         }
1740         else {
1741             set_timeout(b->html_timeout);
1742             g_string_append(b->html_buffer, ctl_line);
1743         }
1744     }
1745     else if((ctl_line[0] == '#') /* Comments */
1746             || (ctl_line[0] == ' ')
1747             || (ctl_line[0] == '\n'))
1748         ; /* ignore these lines */
1749     else { /* parse a command */
1750         gchar *ctlstrip;
1751         gchar **tokens = NULL;
1752         len = strlen(ctl_line);
1753
1754         if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1755             ctlstrip = g_strndup(ctl_line, len - 1);
1756         else ctlstrip = g_strdup(ctl_line);
1757
1758         tokens = g_strsplit(ctlstrip, " ", 2);
1759         parse_command(tokens[0], tokens[1], result);
1760         g_free(ctlstrip);
1761         g_strfreev(tokens);
1762     }
1763 }
1764
1765 static gchar*
1766 build_stream_name(int type, const gchar* dir) {
1767     char *xwin_str;
1768     State *s = &uzbl.state;
1769     gchar *str;
1770
1771     xwin_str = itos((int)uzbl.xwin);
1772     if (type == FIFO) {
1773         str = g_strdup_printf
1774             ("%s/uzbl_fifo_%s", dir,
1775              s->instance_name ? s->instance_name : xwin_str);
1776     } else if (type == SOCKET) {
1777         str = g_strdup_printf
1778             ("%s/uzbl_socket_%s", dir,
1779              s->instance_name ? s->instance_name : xwin_str );
1780     }
1781     g_free(xwin_str);
1782     return str;
1783 }
1784
1785 static gboolean
1786 control_fifo(GIOChannel *gio, GIOCondition condition) {
1787     if (uzbl.state.verbose)
1788         printf("triggered\n");
1789     gchar *ctl_line;
1790     GIOStatus ret;
1791     GError *err = NULL;
1792
1793     if (condition & G_IO_HUP)
1794         g_error ("Fifo: Read end of pipe died!\n");
1795
1796     if(!gio)
1797        g_error ("Fifo: GIOChannel broke\n");
1798
1799     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1800     if (ret == G_IO_STATUS_ERROR) {
1801         g_error ("Fifo: Error reading: %s\n", err->message);
1802         g_error_free (err);
1803     }
1804
1805     parse_cmd_line(ctl_line, NULL);
1806     g_free(ctl_line);
1807
1808     return TRUE;
1809 }
1810
1811 static gchar*
1812 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1813     if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1814         if (unlink(uzbl.comm.fifo_path) == -1)
1815             g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1816         g_free(uzbl.comm.fifo_path);
1817         uzbl.comm.fifo_path = NULL;
1818     }
1819
1820     if (*dir == ' ') { /* space unsets the variable */
1821         g_free (dir);
1822         return NULL;
1823     }
1824
1825     GIOChannel *chan = NULL;
1826     GError *error = NULL;
1827     gchar *path = build_stream_name(FIFO, dir);
1828
1829     if (!file_exists(path)) {
1830         if (mkfifo (path, 0666) == 0) {
1831             // 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.
1832             chan = g_io_channel_new_file(path, "r+", &error);
1833             if (chan) {
1834                 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1835                     if (uzbl.state.verbose)
1836                         printf ("init_fifo: created successfully as %s\n", path);
1837                     uzbl.comm.fifo_path = path;
1838                     return dir;
1839                 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1840             } else g_warning ("init_fifo: can't open: %s\n", error->message);
1841         } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1842     } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1843
1844     /* if we got this far, there was an error; cleanup */
1845     if (error) g_error_free (error);
1846     g_free(dir);
1847     g_free(path);
1848     return NULL;
1849 }
1850
1851 static gboolean
1852 control_stdin(GIOChannel *gio, GIOCondition condition) {
1853     (void) condition;
1854     gchar *ctl_line = NULL;
1855     GIOStatus ret;
1856
1857     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1858     if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1859         return FALSE;
1860
1861     parse_cmd_line(ctl_line, NULL);
1862     g_free(ctl_line);
1863
1864     return TRUE;
1865 }
1866
1867 static void
1868 create_stdin () {
1869     GIOChannel *chan = NULL;
1870     GError *error = NULL;
1871
1872     chan = g_io_channel_unix_new(fileno(stdin));
1873     if (chan) {
1874         if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1875             g_error ("Stdin: could not add watch\n");
1876         } else {
1877             if (uzbl.state.verbose)
1878                 printf ("Stdin: watch added successfully\n");
1879         }
1880     } else {
1881         g_error ("Stdin: Error while opening: %s\n", error->message);
1882     }
1883     if (error) g_error_free (error);
1884 }
1885
1886 static gboolean
1887 control_socket(GIOChannel *chan) {
1888     struct sockaddr_un remote;
1889     unsigned int t = sizeof(remote);
1890     int clientsock;
1891     GIOChannel *clientchan;
1892
1893     clientsock = accept (g_io_channel_unix_get_fd(chan),
1894                          (struct sockaddr *) &remote, &t);
1895     
1896     if ((clientchan = g_io_channel_unix_new(clientsock))) {
1897         g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1898                        (GIOFunc) control_client_socket, clientchan);
1899     }
1900
1901     return TRUE;
1902 }
1903
1904 static gboolean
1905 control_client_socket(GIOChannel *clientchan) {
1906     char *ctl_line;
1907     GString *result = g_string_new("");
1908     GError *error = NULL;
1909     GIOStatus ret;
1910     gsize len;
1911
1912     ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1913     if (ret == G_IO_STATUS_ERROR) {
1914         g_warning ("Error reading: %s\n", error->message);
1915         g_io_channel_shutdown(clientchan, TRUE, &error);
1916         return FALSE;
1917     } else if (ret == G_IO_STATUS_EOF) {
1918         /* shutdown and remove channel watch from main loop */
1919         g_io_channel_shutdown(clientchan, TRUE, &error);
1920         return FALSE;
1921     }
1922
1923     if (ctl_line) {
1924         parse_cmd_line (ctl_line, result);
1925         g_string_append_c(result, '\n');
1926         ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1927                                         &len, &error);
1928         if (ret == G_IO_STATUS_ERROR) {
1929             g_warning ("Error writing: %s", error->message);
1930         }
1931         g_io_channel_flush(clientchan, &error);
1932     }
1933
1934     if (error) g_error_free (error);
1935     g_string_free(result, TRUE);
1936     g_free(ctl_line);
1937     return TRUE;
1938 }
1939
1940 static gchar*
1941 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1942     if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1943         if (unlink(uzbl.comm.socket_path) == -1)
1944             g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1945         g_free(uzbl.comm.socket_path);
1946         uzbl.comm.socket_path = NULL;
1947     }
1948
1949     if (*dir == ' ') {
1950         g_free(dir);
1951         return NULL;
1952     }
1953
1954     GIOChannel *chan = NULL;
1955     int sock, len;
1956     struct sockaddr_un local;
1957     gchar *path = build_stream_name(SOCKET, dir);
1958
1959     sock = socket (AF_UNIX, SOCK_STREAM, 0);
1960
1961     local.sun_family = AF_UNIX;
1962     strcpy (local.sun_path, path);
1963     unlink (local.sun_path);
1964
1965     len = strlen (local.sun_path) + sizeof (local.sun_family);
1966     if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1967         if (uzbl.state.verbose)
1968             printf ("init_socket: opened in %s\n", path);
1969         listen (sock, 5);
1970
1971         if( (chan = g_io_channel_unix_new(sock)) ) {
1972             g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1973             uzbl.comm.socket_path = path;
1974             return dir;
1975         }
1976     } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1977
1978     /* if we got this far, there was an error; cleanup */
1979     g_free(path);
1980     g_free(dir);
1981     return NULL;
1982 }
1983
1984 /*
1985  NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1986  it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1987 */
1988 // this function may be called very early when the templates are not set (yet), hence the checks
1989 static void
1990 update_title (void) {
1991     Behaviour *b = &uzbl.behave;
1992     gchar *parsed;
1993
1994     if (b->show_status) {
1995         if (b->title_format_short) {
1996             parsed = expand_template(b->title_format_short, FALSE);
1997             if (uzbl.gui.main_window)
1998                 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1999             g_free(parsed);
2000         }
2001         if (b->status_format) {
2002             parsed = expand_template(b->status_format, TRUE);
2003             gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2004             g_free(parsed);
2005         }
2006         if (b->status_background) {
2007             GdkColor color;
2008             gdk_color_parse (b->status_background, &color);
2009             //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)
2010             if (uzbl.gui.main_window)
2011                 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2012         }
2013     } else {
2014         if (b->title_format_long) {
2015             parsed = expand_template(b->title_format_long, FALSE);
2016             if (uzbl.gui.main_window)
2017                 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2018             g_free(parsed);
2019         }
2020     }
2021 }
2022
2023 static gboolean
2024 key_press_cb (GtkWidget* window, GdkEventKey* event)
2025 {
2026     //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2027
2028     (void) window;
2029
2030     if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2031         || 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)
2032         return FALSE;
2033
2034     /* turn off insert mode (if always_insert_mode is not used) */
2035     if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2036         uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
2037         update_title();
2038         return TRUE;
2039     }
2040
2041     if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2042         return FALSE;
2043
2044     if (event->keyval == GDK_Escape) {
2045         g_string_truncate(uzbl.state.keycmd, 0);
2046         update_title();
2047         dehilight(uzbl.gui.web_view, NULL, NULL);
2048         return TRUE;
2049     }
2050
2051     //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2052     if (event->keyval == GDK_Insert) {
2053         gchar * str;
2054         if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2055             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2056         } else {
2057             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2058         }
2059         if (str) {
2060             g_string_append (uzbl.state.keycmd, str);
2061             update_title ();
2062             g_free (str);
2063         }
2064         return TRUE;
2065     }
2066
2067     if (event->keyval == GDK_BackSpace)
2068         keycmd_bs(NULL, NULL, NULL);
2069
2070     gboolean key_ret = FALSE;
2071     if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2072         key_ret = TRUE;
2073     if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2074
2075     run_keycmd(key_ret);
2076     update_title();
2077     if (key_ret) return (!uzbl.behave.insert_mode);
2078     return TRUE;
2079 }
2080
2081 static void
2082 run_keycmd(const gboolean key_ret) {
2083     /* run the keycmd immediately if it isn't incremental and doesn't take args */
2084     Action *act;
2085     if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2086         g_string_truncate(uzbl.state.keycmd, 0);
2087         parse_command(act->name, act->param, NULL);
2088         return;
2089     }
2090
2091     /* try if it's an incremental keycmd or one that takes args, and run it */
2092     GString* short_keys = g_string_new ("");
2093     GString* short_keys_inc = g_string_new ("");
2094     guint i;
2095     for (i=0; i<(uzbl.state.keycmd->len); i++) {
2096         g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2097         g_string_assign(short_keys_inc, short_keys->str);
2098         g_string_append_c(short_keys, '_');
2099         g_string_append_c(short_keys_inc, '*');
2100
2101         if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2102             /* run normal cmds only if return was pressed */
2103             exec_paramcmd(act, i);
2104             g_string_truncate(uzbl.state.keycmd, 0);
2105             break;
2106         } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2107             if (key_ret)  /* just quit the incremental command on return */
2108                 g_string_truncate(uzbl.state.keycmd, 0);
2109             else exec_paramcmd(act, i); /* otherwise execute the incremental */
2110             break;
2111         }
2112         
2113         g_string_truncate(short_keys, short_keys->len - 1);
2114     }
2115     g_string_free (short_keys, TRUE);
2116     g_string_free (short_keys_inc, TRUE);
2117 }
2118
2119 static void
2120 exec_paramcmd(const Action *act, const guint i) {
2121     GString *parampart = g_string_new (uzbl.state.keycmd->str);
2122     GString *actionname = g_string_new ("");
2123     GString *actionparam = g_string_new ("");
2124     g_string_erase (parampart, 0, i+1);
2125     if (act->name)
2126         g_string_printf (actionname, act->name, parampart->str);
2127     if (act->param)
2128         g_string_printf (actionparam, act->param, parampart->str);
2129     parse_command(actionname->str, actionparam->str, NULL);
2130     g_string_free(actionname, TRUE);
2131     g_string_free(actionparam, TRUE);
2132     g_string_free(parampart, TRUE);
2133 }
2134
2135
2136 static GtkWidget*
2137 create_browser () {
2138     GUI *g = &uzbl.gui;
2139
2140     GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2141     //main_window_ref = g_object_ref(scrolled_window);
2142     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
2143
2144     g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2145     gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2146
2147     g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
2148     g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2149     g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2150     g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2151     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2152     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2153     g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2154     g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2155     g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2156     g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2157     g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2158
2159     return scrolled_window;
2160 }
2161
2162 static GtkWidget*
2163 create_mainbar () {
2164     GUI *g = &uzbl.gui;
2165
2166     g->mainbar = gtk_hbox_new (FALSE, 0);
2167
2168     /* keep a reference to the bar so we can re-pack it at runtime*/
2169     //sbar_ref = g_object_ref(g->mainbar);
2170
2171     g->mainbar_label = gtk_label_new ("");
2172     gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2173     gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2174     gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2175     gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2176     gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2177     g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2178     return g->mainbar;
2179 }
2180
2181 static
2182 GtkWidget* create_window () {
2183     GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2184     gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2185     gtk_widget_set_name (window, "Uzbl browser");
2186     g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2187     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2188
2189     return window;
2190 }
2191
2192 static
2193 GtkPlug* create_plug () {
2194     GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2195     g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2196     g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2197
2198     return plug;
2199 }
2200
2201
2202 static gchar**
2203 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2204     /*
2205       If actname is one that calls an external command, this function will inject
2206       newargs in front of the user-provided args in that command line.  They will
2207       come become after the body of the script (in sh) or after the name of
2208       the command to execute (in spawn).
2209       i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2210       span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2211
2212       The return value consist of two strings: the action (sh, ...) and its args.
2213
2214       If act is not one that calls an external command, then the given action merely
2215       gets duplicated.
2216     */
2217     GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2218     gchar *actdup = g_strdup(actname);
2219     g_array_append_val(rets, actdup);
2220
2221     if ((g_strcmp0(actname, "spawn") == 0) ||
2222         (g_strcmp0(actname, "sh") == 0) ||
2223         (g_strcmp0(actname, "sync_spawn") == 0) ||
2224         (g_strcmp0(actname, "sync_sh") == 0)) {
2225         guint i;
2226         GString *a = g_string_new("");
2227         gchar **spawnparts = split_quoted(origargs, FALSE);
2228         g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2229         if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2230
2231         for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2232             if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2233
2234         g_array_append_val(rets, a->str);
2235         g_string_free(a, FALSE);
2236         g_strfreev(spawnparts);
2237     } else {
2238         gchar *origdup = g_strdup(origargs);
2239         g_array_append_val(rets, origdup);
2240     }
2241     return (gchar**)g_array_free(rets, FALSE);
2242 }
2243
2244 static void
2245 run_handler (const gchar *act, const gchar *args) {
2246     /* Consider this code a temporary hack to make the handlers usable.
2247        In practice, all this splicing, injection, and reconstruction is
2248        inefficient, annoying and hard to manage.  Potential pitfalls arise
2249        when the handler specific args 1) are not quoted  (the handler
2250        callbacks should take care of this)  2) are quoted but interfere
2251        with the users' own quotation.  A more ideal solution is
2252        to refactor parse_command so that it doesn't just take a string
2253        and execute it; rather than that, we should have a function which
2254        returns the argument vector parsed from the string.  This vector
2255        could be modified (e.g. insert additional args into it) before
2256        passing it to the next function that actually executes it.  Though
2257        it still isn't perfect for chain actions..  will reconsider & re-
2258        factor when I have the time. -duc */
2259
2260     char **parts = g_strsplit(act, " ", 2);
2261     if (!parts) return;
2262     if (g_strcmp0(parts[0], "chain") == 0) {
2263         GString *newargs = g_string_new("");
2264         gchar **chainparts = split_quoted(parts[1], FALSE);
2265         
2266         /* for every argument in the chain, inject the handler args
2267            and make sure the new parts are wrapped in quotes */
2268         gchar **cp = chainparts;
2269         gchar quot = '\'';
2270         gchar *quotless = NULL;
2271         gchar **spliced_quotless = NULL; // sigh -_-;
2272         gchar **inpart = NULL;
2273         
2274         while (*cp) {
2275             if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2276                 quot = **cp;
2277                 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2278             } else quotless = g_strdup(*cp);
2279
2280             spliced_quotless = g_strsplit(quotless, " ", 2);
2281             inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2282             g_strfreev(spliced_quotless);
2283             
2284             g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2285             g_free(quotless);
2286             g_strfreev(inpart);
2287             cp++;
2288         }
2289
2290         parse_command(parts[0], &(newargs->str[1]), NULL);
2291         g_string_free(newargs, TRUE);
2292         g_strfreev(chainparts);
2293         
2294     } else {
2295         gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2296         parse_command(inparts[0], inparts[1], NULL);
2297         g_free(inparts[0]);
2298         g_free(inparts[1]);
2299     }
2300     g_strfreev(parts);
2301 }
2302
2303 static void
2304 add_binding (const gchar *key, const gchar *act) {
2305     char **parts = g_strsplit(act, " ", 2);
2306     Action *action;
2307
2308     if (!parts)
2309         return;
2310
2311     //Debug:
2312     if (uzbl.state.verbose)
2313         printf ("Binding %-10s : %s\n", key, act);
2314     action = new_action(parts[0], parts[1]);
2315
2316     if (g_hash_table_remove (uzbl.bindings, key))
2317         g_warning ("Overwriting existing binding for \"%s\"", key);
2318     g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2319     g_strfreev(parts);
2320 }
2321
2322 static gchar*
2323 get_xdg_var (XDG_Var xdg) {
2324     const gchar* actual_value = getenv (xdg.environmental);
2325     const gchar* home         = getenv ("HOME");
2326     gchar* return_value;
2327
2328     if (! actual_value || strcmp (actual_value, "") == 0) {
2329         if (xdg.default_value) {
2330             return_value = str_replace ("~", home, xdg.default_value);
2331         } else {
2332             return_value = NULL;
2333         }
2334     } else {
2335         return_value = str_replace("~", home, actual_value);
2336     }
2337
2338     return return_value;
2339 }
2340
2341 static gchar*
2342 find_xdg_file (int xdg_type, char* filename) {
2343     /* xdg_type = 0 => config
2344        xdg_type = 1 => data
2345        xdg_type = 2 => cache*/
2346
2347     gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2348     gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2349     g_free (xdgv);
2350
2351     gchar* temporary_string;
2352     char*  saveptr;
2353     char*  buf;
2354
2355     if (! file_exists (temporary_file) && xdg_type != 2) {
2356         buf = get_xdg_var (XDG[3 + xdg_type]);
2357         temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2358         g_free(buf);
2359
2360         while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2361             g_free (temporary_file);
2362             temporary_file = g_strconcat (temporary_string, filename, NULL);
2363         }
2364     }
2365     
2366     //g_free (temporary_string); - segfaults.
2367
2368     if (file_exists (temporary_file)) {
2369         return temporary_file;
2370     } else {
2371         return NULL;
2372     }
2373 }
2374 static void
2375 settings_init () {
2376     State *s = &uzbl.state;
2377     Network *n = &uzbl.net;
2378     int i;
2379     for (i = 0; default_config[i].command != NULL; i++) {
2380         parse_cmd_line(default_config[i].command, NULL);
2381     }
2382
2383     if (g_strcmp0(s->config_file, "-") == 0) {
2384         s->config_file = NULL;
2385         create_stdin();
2386     }
2387
2388     if (!s->config_file) {
2389         s->config_file = find_xdg_file (0, "/uzbl/config");
2390     }
2391
2392     if (s->config_file) {
2393         GArray* lines = read_file_by_line (s->config_file);
2394         int i = 0;
2395         gchar* line;
2396
2397         while ((line = g_array_index(lines, gchar*, i))) {
2398             parse_cmd_line (line, NULL);
2399             i ++;
2400             g_free (line);
2401         }
2402         g_array_free (lines, TRUE);
2403     } else {
2404         if (uzbl.state.verbose)
2405             printf ("No configuration file loaded.\n");
2406     }
2407
2408     g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2409 }
2410
2411 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2412     (void) session;
2413     (void) user_data;
2414     if (!uzbl.behave.cookie_handler)
2415          return;
2416
2417     soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2418     GString *s = g_string_new ("");
2419     SoupURI * soup_uri = soup_message_get_uri(msg);
2420     g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2421     run_handler(uzbl.behave.cookie_handler, s->str);
2422
2423     if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2424         char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2425         if ( p != NULL ) *p = '\0';
2426         soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2427     }
2428     if (uzbl.comm.sync_stdout)
2429         uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2430         
2431     g_string_free(s, TRUE);
2432 }
2433
2434 static void
2435 save_cookies (SoupMessage *msg, gpointer user_data){
2436     (void) user_data;
2437     GSList *ck;
2438     char *cookie;
2439     for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2440         cookie = soup_cookie_to_set_cookie_header(ck->data);
2441         SoupURI * soup_uri = soup_message_get_uri(msg);
2442         GString *s = g_string_new ("");
2443         g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2444         run_handler(uzbl.behave.cookie_handler, s->str);
2445         g_free (cookie);
2446         g_string_free(s, TRUE);
2447     }
2448     g_slist_free(ck);
2449 }
2450
2451 /* --- WEBINSPECTOR --- */
2452 static void
2453 hide_window_cb(GtkWidget *widget, gpointer data) {
2454     (void) data;
2455
2456     gtk_widget_hide(widget);
2457 }
2458
2459 static WebKitWebView*
2460 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2461     (void) data;
2462     (void) page;
2463     (void) web_inspector;
2464     GtkWidget* scrolled_window;
2465     GtkWidget* new_web_view;
2466     GUI *g = &uzbl.gui;
2467
2468     g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2469     g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2470             G_CALLBACK(hide_window_cb), NULL);
2471
2472     gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2473     gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2474     gtk_widget_show(g->inspector_window);
2475
2476     scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2477     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2478             GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2479     gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2480     gtk_widget_show(scrolled_window);
2481
2482     new_web_view = webkit_web_view_new();
2483     gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2484
2485     return WEBKIT_WEB_VIEW(new_web_view);
2486 }
2487
2488 static gboolean
2489 inspector_show_window_cb (WebKitWebInspector* inspector){
2490     (void) inspector;
2491     gtk_widget_show(uzbl.gui.inspector_window);
2492     return TRUE;
2493 }
2494
2495 /* TODO: Add variables and code to make use of these functions */
2496 static gboolean
2497 inspector_close_window_cb (WebKitWebInspector* inspector){
2498     (void) inspector;
2499     return TRUE;
2500 }
2501
2502 static gboolean
2503 inspector_attach_window_cb (WebKitWebInspector* inspector){
2504     (void) inspector;
2505     return FALSE;
2506 }
2507
2508 static gboolean
2509 inspector_detach_window_cb (WebKitWebInspector* inspector){
2510     (void) inspector;
2511     return FALSE;
2512 }
2513
2514 static gboolean
2515 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2516     (void) inspector;
2517     return FALSE;
2518 }
2519
2520 static gboolean
2521 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2522     (void) inspector;
2523     return FALSE;
2524 }
2525
2526 static void
2527 set_up_inspector() {
2528     GUI *g = &uzbl.gui;
2529     WebKitWebSettings *settings = view_settings();
2530     g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2531
2532     uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2533     g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2534     g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2535     g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2536     g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2537     g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2538     g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2539
2540     g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2541 }
2542
2543 static void
2544 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2545     (void) ud;
2546     uzbl_cmdprop *c = v;
2547
2548     if(!c->dump)
2549         return;
2550
2551     if(c->type == TYPE_STR)
2552         printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2553     else if(c->type == TYPE_INT)
2554         printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2555 }
2556
2557 static void
2558 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2559     (void) ud;
2560     Action *a = v;
2561
2562     printf("bind %s = %s %s\n", (char *)k ,
2563             (char *)a->name, a->param?(char *)a->param:"");
2564 }
2565
2566 static void
2567 dump_config() {
2568     g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2569     g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2570 }
2571
2572 /** -- MAIN -- **/
2573 int
2574 main (int argc, char* argv[]) {
2575     gtk_init (&argc, &argv);
2576     if (!g_thread_supported ())
2577         g_thread_init (NULL);
2578     uzbl.state.executable_path = g_strdup(argv[0]);
2579     uzbl.state.selected_url = NULL;
2580     uzbl.state.searchtx = NULL;
2581
2582     GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2583     g_option_context_add_main_entries (context, entries, NULL);
2584     g_option_context_add_group (context, gtk_get_option_group (TRUE));
2585     g_option_context_parse (context, &argc, &argv, NULL);
2586     g_option_context_free(context);
2587     
2588     gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2589     gboolean verbose_override = uzbl.state.verbose;
2590
2591     /* initialize hash table */
2592     uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2593
2594     uzbl.net.soup_session = webkit_get_default_session();
2595     uzbl.state.keycmd = g_string_new("");
2596
2597     if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2598         fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2599     if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2600         fprintf(stderr, "uzbl: error hooking SIGINT\n");
2601     if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2602         fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2603
2604
2605     if(uname(&uzbl.state.unameinfo) == -1)
2606         g_printerr("Can't retrieve unameinfo.  Your useragent might appear wrong.\n");
2607
2608     uzbl.gui.sbar.progress_s = g_strdup("=");
2609     uzbl.gui.sbar.progress_u = g_strdup("ยท");
2610     uzbl.gui.sbar.progress_w = 10;
2611
2612     /* HTML mode defaults*/
2613     uzbl.behave.html_buffer = g_string_new("");
2614     uzbl.behave.html_endmarker = g_strdup(".");
2615     uzbl.behave.html_timeout = 60;
2616     uzbl.behave.base_url = g_strdup("http://invalid");
2617
2618     /* default mode indicators */
2619     uzbl.behave.insert_indicator = g_strdup("I");
2620     uzbl.behave.cmd_indicator    = g_strdup("C");
2621
2622     setup_scanner();
2623     commands_hash ();
2624     make_var_to_name_hash();
2625
2626     uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2627
2628     uzbl.gui.scrolled_win = create_browser();
2629     create_mainbar();
2630
2631     /* initial packing */
2632     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2633     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2634
2635     if (uzbl.state.socket_id) {
2636         uzbl.gui.plug = create_plug ();
2637         gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2638         gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2639     } else {
2640         uzbl.gui.main_window = create_window ();
2641         gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2642         gtk_widget_show_all (uzbl.gui.main_window);
2643         uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2644     }
2645
2646     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2647
2648     if (uzbl.state.verbose) {
2649         printf("Uzbl start location: %s\n", argv[0]);
2650         if (uzbl.state.socket_id)
2651             printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2652         else
2653             printf("window_id %i\n",(int) uzbl.xwin);
2654         printf("pid %i\n", getpid ());
2655         printf("name: %s\n", uzbl.state.instance_name);
2656     }
2657
2658     uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2659     uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2660     uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2661     uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2662     gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2663
2664     settings_init ();
2665
2666     if (!uzbl.behave.show_status)
2667         gtk_widget_hide(uzbl.gui.mainbar);
2668     else
2669         update_title();
2670
2671     /* WebInspector */
2672     set_up_inspector();
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: */