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