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