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