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