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