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