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