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