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