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