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