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