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