Make parse_cmd_line parse as actions
[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     { "set",                {set_var, NOSPLIT}             },
616     { "get",                {get_var, NOSPLIT}             },
617     { "bind",               {act_bind, 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     gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
669     set_var_value(g_strstrip(split[0]), value);
670     g_free(value);
671     g_strfreev(split);
672 }
673
674 static void
675 get_var(WebKitWebView *page, GArray *argv) {
676     (void) page;
677     get_var_value(argv_idx(argv, 0));
678 }
679
680 static void
681 act_bind(WebKitWebView *page, GArray *argv) {
682     (void) page;
683     gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
684     gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
685     add_binding(g_strstrip(split[0]), value);
686     g_free(value);
687     g_strfreev(split);
688 }
689
690
691 static void
692 act_dump_config() {
693     dump_config();
694 }
695
696 static void
697 toggle_insert_mode(WebKitWebView *page, GArray *argv) {
698     (void)page;
699
700     if (argv_idx(argv, 0)) {
701         if (strcmp (argv_idx(argv, 0), "0") == 0) {
702             uzbl.behave.insert_mode = FALSE;
703         } else {
704             uzbl.behave.insert_mode = TRUE;
705         }
706     } else {
707         uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
708     }
709
710     update_title();
711 }
712
713 static void
714 load_uri (WebKitWebView *web_view, GArray *argv) {
715     if (argv_idx(argv, 0)) {
716         GString* newuri = g_string_new (argv_idx(argv, 0));
717         if (g_strrstr (argv_idx(argv, 0), "://") == NULL)
718             g_string_prepend (newuri, "http://");
719         /* if we do handle cookies, ask our handler for them */
720         webkit_web_view_load_uri (web_view, newuri->str);
721         g_string_free (newuri, TRUE);
722     }
723 }
724
725 static void
726 run_js (WebKitWebView * web_view, GArray *argv) {
727     if (argv_idx(argv, 0))
728         webkit_web_view_execute_script (web_view, argv_idx(argv, 0));
729 }
730
731 static void
732 run_external_js (WebKitWebView * web_view, GArray *argv) {
733     if (argv_idx(argv, 0)) {
734         GArray* lines = read_file_by_line (argv_idx (argv, 0));
735         gchar*  js = NULL;
736         int i = 0;
737         gchar* line;
738
739         while ((line = g_array_index(lines, gchar*, i))) {
740             if (js == NULL) {
741                 js = g_strdup (line);
742             } else {
743                 gchar* newjs = g_strconcat (js, line, NULL);
744                 js = newjs;
745             }
746             i ++;
747             g_free (line);
748         }
749         
750         if (uzbl.state.verbose)
751             printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
752
753         if (argv_idx (argv, 1)) {
754             gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
755             g_free (js);
756             js = newjs;
757         }
758         webkit_web_view_execute_script (web_view, js);
759         g_free (js);
760         g_array_free (lines, TRUE);
761     }
762 }
763
764 static void
765 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
766     if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
767         if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
768             webkit_web_view_unmark_text_matches (page);
769             webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
770             uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
771         }
772     }
773     
774     if (uzbl.state.searchtx) {
775         if (uzbl.state.verbose)
776             printf ("Searching: %s\n", uzbl.state.searchtx);
777         webkit_web_view_set_highlight_text_matches (page, TRUE);
778         webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
779     }
780 }
781
782 static void
783 search_forward_text (WebKitWebView *page, GArray *argv) {
784     search_text(page, argv, TRUE);
785 }
786
787 static void
788 search_reverse_text (WebKitWebView *page, GArray *argv) {
789     search_text(page, argv, FALSE);
790 }
791
792 static void
793 dehilight (WebKitWebView *page, GArray *argv) {
794     (void) argv;
795     webkit_web_view_set_highlight_text_matches (page, FALSE);
796 }
797
798
799 static void
800 new_window_load_uri (const gchar * uri) {
801     GString* to_execute = g_string_new ("");
802     g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
803     int i;
804     for (i = 0; entries[i].long_name != NULL; i++) {
805         if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
806             gchar** str = (gchar**)entries[i].arg_data;
807             if (*str!=NULL) {
808                 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
809             }
810         }
811     }
812     if (uzbl.state.verbose)
813         printf("\n%s\n", to_execute->str);
814     g_spawn_command_line_async (to_execute->str, NULL);
815     g_string_free (to_execute, TRUE);
816 }
817
818 static void
819 chain (WebKitWebView *page, GArray *argv) {
820     (void)page;
821     gchar *a = NULL;
822     gchar **parts = NULL;
823     guint i = 0;    
824     while ((a = argv_idx(argv, i++))) {
825         parts = g_strsplit (a, " ", 2);
826         parse_command(parts[0], parts[1]);
827         g_strfreev (parts);
828     }
829 }
830
831 static void
832 keycmd (WebKitWebView *page, GArray *argv) {
833     (void)page;
834     (void)argv;
835     g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
836     run_keycmd(FALSE);
837     update_title();
838 }
839
840 static void
841 keycmd_nl (WebKitWebView *page, GArray *argv) {
842     (void)page;
843     (void)argv;
844     g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
845     run_keycmd(TRUE);
846     update_title();
847 }
848
849 static void
850 keycmd_bs (WebKitWebView *page, GArray *argv) {
851     (void)page;
852     (void)argv;
853     g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
854     update_title();
855 }
856
857 static void
858 close_uzbl (WebKitWebView *page, GArray *argv) {
859     (void)page;
860     (void)argv;
861     gtk_main_quit ();
862 }
863
864 /* --Statusbar functions-- */
865 static char*
866 build_progressbar_ascii(int percent) {
867    int width=uzbl.gui.sbar.progress_w;
868    int i;
869    double l;
870    GString *bar = g_string_new("");
871
872    l = (double)percent*((double)width/100.);
873    l = (int)(l+.5)>=(int)l ? l+.5 : l;
874
875    for(i=0; i<(int)l; i++)
876        g_string_append(bar, uzbl.gui.sbar.progress_s);
877
878    for(; i<width; i++)
879        g_string_append(bar, uzbl.gui.sbar.progress_u);
880
881    return g_string_free(bar, FALSE);
882 }
883
884 static void
885 setup_scanner() {
886      const GScannerConfig scan_config = {
887              (
888               "\t\r\n"
889              )            /* cset_skip_characters */,
890              (
891               G_CSET_a_2_z
892               "_#"
893               G_CSET_A_2_Z
894              )            /* cset_identifier_first */,
895              (
896               G_CSET_a_2_z
897               "_0123456789"
898               G_CSET_A_2_Z
899               G_CSET_LATINS
900               G_CSET_LATINC
901              )            /* cset_identifier_nth */,
902              ( "" )    /* cpair_comment_single */,
903
904              TRUE         /* case_sensitive */,
905
906              FALSE        /* skip_comment_multi */,
907              FALSE        /* skip_comment_single */,
908              FALSE        /* scan_comment_multi */,
909              TRUE         /* scan_identifier */,
910              TRUE         /* scan_identifier_1char */,
911              FALSE        /* scan_identifier_NULL */,
912              TRUE         /* scan_symbols */,
913              FALSE        /* scan_binary */,
914              FALSE        /* scan_octal */,
915              FALSE        /* scan_float */,
916              FALSE        /* scan_hex */,
917              FALSE        /* scan_hex_dollar */,
918              FALSE        /* scan_string_sq */,
919              FALSE        /* scan_string_dq */,
920              TRUE         /* numbers_2_int */,
921              FALSE        /* int_2_float */,
922              FALSE        /* identifier_2_string */,
923              FALSE        /* char_2_token */,
924              FALSE        /* symbol_2_token */,
925              TRUE         /* scope_0_fallback */,
926              FALSE,
927              TRUE
928      };
929
930      uzbl.scan = g_scanner_new(&scan_config);
931      while(symp->symbol_name) {
932          g_scanner_scope_add_symbol(uzbl.scan, 0,
933                          symp->symbol_name,
934                          GINT_TO_POINTER(symp->symbol_token));
935          symp++;
936      }
937 }
938
939 static gchar *
940 expand_template(const char *template, gboolean escape_markup) {
941      if(!template) return NULL;
942
943      GTokenType token = G_TOKEN_NONE;
944      GString *ret = g_string_new("");
945      char *buf=NULL;
946      int sym;
947
948      g_scanner_input_text(uzbl.scan, template, strlen(template));
949      while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
950          token = g_scanner_get_next_token(uzbl.scan);
951
952          if(token == G_TOKEN_SYMBOL) {
953              sym = (int)g_scanner_cur_value(uzbl.scan).v_symbol;
954              switch(sym) {
955                  case SYM_URI:
956                      if(escape_markup) {
957                          buf = uzbl.state.uri?
958                              g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
959                          g_string_append(ret, buf);
960                          g_free(buf);
961                      }
962                      else
963                          g_string_append(ret, uzbl.state.uri?
964                                  uzbl.state.uri:g_strdup(""));
965                      break;
966                  case SYM_LOADPRGS:
967                      buf = itos(uzbl.gui.sbar.load_progress);
968                      g_string_append(ret, buf);
969                      g_free(buf);
970                      break;
971                  case SYM_LOADPRGSBAR:
972                      buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
973                      g_string_append(ret, buf);
974                      g_free(buf);
975                      break;
976                  case SYM_TITLE:
977                      if(escape_markup) {
978                          buf = uzbl.gui.main_title?
979                              g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
980                          g_string_append(ret, buf);
981                          g_free(buf);
982                      }
983                      else
984                          g_string_append(ret, uzbl.gui.main_title?
985                                  uzbl.gui.main_title:g_strdup(""));
986                      break;
987                  case SYM_SELECTED_URI:
988                      if(escape_markup) {
989                          buf = uzbl.state.selected_url?
990                              g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
991                          g_string_append(ret, buf);
992                          g_free(buf);
993                      }
994                      else
995                          g_string_append(ret, uzbl.state.selected_url?
996                                  uzbl.state.selected_url:g_strdup(""));
997                      break;
998                  case SYM_NAME:
999                      buf = itos(uzbl.xwin);
1000                      g_string_append(ret,
1001                              uzbl.state.instance_name?uzbl.state.instance_name:buf);
1002                      g_free(buf);
1003                      break;
1004                  case SYM_KEYCMD:
1005                      if(escape_markup) {
1006                          buf = uzbl.state.keycmd->str?
1007                              g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1008                          g_string_append(ret, buf);
1009                          g_free(buf);
1010                      }
1011                      else
1012                          g_string_append(ret, uzbl.state.keycmd->str?
1013                                  uzbl.state.keycmd->str:g_strdup(""));
1014                      break;
1015                  case SYM_MODE:
1016                      g_string_append(ret,
1017                              uzbl.behave.insert_mode?
1018                              uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1019                      break;
1020                  case SYM_MSG:
1021                      g_string_append(ret,
1022                              uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1023                      break;
1024                      /* useragent syms */
1025                  case SYM_WK_MAJ:
1026                      buf = itos(WEBKIT_MAJOR_VERSION);
1027                      g_string_append(ret, buf);
1028                      g_free(buf);
1029                      break;
1030                  case SYM_WK_MIN:
1031                      buf = itos(WEBKIT_MINOR_VERSION);
1032                      g_string_append(ret, buf);
1033                      g_free(buf);
1034                      break;
1035                  case SYM_WK_MIC:
1036                      buf = itos(WEBKIT_MICRO_VERSION);
1037                      g_string_append(ret, buf);
1038                      g_free(buf);
1039                      break;
1040                  case SYM_SYSNAME:
1041                      g_string_append(ret, uzbl.state.unameinfo.sysname);
1042                      break;
1043                  case SYM_NODENAME:
1044                      g_string_append(ret, uzbl.state.unameinfo.nodename);
1045                      break;
1046                  case SYM_KERNREL:
1047                      g_string_append(ret, uzbl.state.unameinfo.release);
1048                      break;
1049                  case SYM_KERNVER:
1050                      g_string_append(ret, uzbl.state.unameinfo.version);
1051                      break;
1052                  case SYM_ARCHSYS:
1053                      g_string_append(ret, uzbl.state.unameinfo.machine);
1054                      break;
1055                  case SYM_ARCHUZBL:
1056                      g_string_append(ret, ARCH);
1057                      break;
1058 #ifdef _GNU_SOURCE
1059                  case SYM_DOMAINNAME:
1060                      g_string_append(ret, uzbl.state.unameinfo.domainname);
1061                      break;
1062 #endif
1063                  case SYM_COMMIT:
1064                      g_string_append(ret, COMMIT);
1065                      break;
1066                  default:
1067                      break;
1068              }
1069          }
1070          else if(token == G_TOKEN_INT) {
1071              buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1072              g_string_append(ret, buf);
1073              g_free(buf);
1074          }
1075          else if(token == G_TOKEN_IDENTIFIER) {
1076              g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1077          }
1078          else if(token == G_TOKEN_CHAR) {
1079              g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1080          }
1081      }
1082
1083      return g_string_free(ret, FALSE);
1084 }
1085 /* --End Statusbar functions-- */
1086
1087 static void
1088 sharg_append(GArray *a, const gchar *str) {
1089     const gchar *s = (str ? str : "");
1090     g_array_append_val(a, s);
1091 }
1092
1093 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1094 static gboolean
1095 run_command (const gchar *command, const guint npre, const gchar **args,
1096              const gboolean sync, char **stdout) {
1097    //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1098     GError *err = NULL;
1099     
1100     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1101     gchar *pid = itos(getpid());
1102     gchar *xwin = itos(uzbl.xwin);
1103     guint i;
1104     sharg_append(a, command);
1105     for (i = 0; i < npre; i++) /* add n args before the default vars */
1106         sharg_append(a, args[i]);
1107     sharg_append(a, uzbl.state.config_file);
1108     sharg_append(a, pid);
1109     sharg_append(a, xwin);
1110     sharg_append(a, uzbl.comm.fifo_path);
1111     sharg_append(a, uzbl.comm.socket_path);
1112     sharg_append(a, uzbl.state.uri);
1113     sharg_append(a, uzbl.gui.main_title);
1114
1115     for (i = npre; i < g_strv_length((gchar**)args); i++)
1116         sharg_append(a, args[i]);
1117     
1118     gboolean result;
1119     if (sync) {
1120         if (*stdout) *stdout = strfree(*stdout);
1121         
1122         result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1123                               NULL, NULL, stdout, NULL, NULL, &err);
1124     } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1125                                   NULL, NULL, NULL, &err);
1126
1127     if (uzbl.state.verbose) {
1128         GString *s = g_string_new("spawned:");
1129         for (i = 0; i < (a->len); i++) {
1130             gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1131             g_string_append_printf(s, " %s", qarg);
1132             g_free (qarg);
1133         }
1134         g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1135         printf("%s\n", s->str);
1136         g_string_free(s, TRUE);
1137     }
1138     if (err) {
1139         g_printerr("error on run_command: %s\n", err->message);
1140         g_error_free (err);
1141     }
1142     g_free (pid);
1143     g_free (xwin);
1144     g_array_free (a, TRUE);
1145     return result;
1146 }
1147
1148 static gchar**
1149 split_quoted(const gchar* src, const gboolean unquote) {
1150     /* split on unquoted space, return array of strings;
1151        remove a layer of quotes and backslashes if unquote */
1152     if (!src) return NULL;
1153     
1154     gboolean dq = FALSE;
1155     gboolean sq = FALSE;
1156     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1157     GString *s = g_string_new ("");
1158     const gchar *p;
1159     gchar **ret;
1160     gchar *dup;
1161     for (p = src; *p != '\0'; p++) {
1162         if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1163         else if (*p == '\\') { g_string_append_c(s, *p++);
1164                                g_string_append_c(s, *p); }
1165         else if ((*p == '"') && unquote && !sq) dq = !dq;
1166         else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1167                                      dq = !dq; }
1168         else if ((*p == '\'') && unquote && !dq) sq = !sq;
1169         else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1170                                       sq = ! sq; }
1171         else if ((*p == ' ') && !dq && !sq) {
1172             dup = g_strdup(s->str);
1173             g_array_append_val(a, dup);
1174             g_string_truncate(s, 0);
1175         } else g_string_append_c(s, *p);
1176     }
1177     dup = g_strdup(s->str);
1178     g_array_append_val(a, dup);
1179     ret = (gchar**)a->data;
1180     g_array_free (a, FALSE);
1181     g_string_free (s, TRUE);
1182     return ret;
1183 }
1184
1185 static void
1186 spawn(WebKitWebView *web_view, GArray *argv) {
1187     (void)web_view;
1188     //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1189     if (argv_idx(argv, 0))
1190         run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1191 }
1192
1193 static void
1194 spawn_sync(WebKitWebView *web_view, GArray *argv) {
1195     (void)web_view;
1196     
1197     if (argv_idx(argv, 0))
1198         run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1199                     TRUE, &uzbl.comm.sync_stdout);
1200 }
1201
1202 static void
1203 spawn_sh(WebKitWebView *web_view, GArray *argv) {
1204     (void)web_view;
1205     if (!uzbl.behave.shell_cmd) {
1206         g_printerr ("spawn_sh: shell_cmd is not set!\n");
1207         return;
1208     }
1209     
1210     guint i;
1211     gchar *spacer = g_strdup("");
1212     g_array_insert_val(argv, 1, spacer);
1213     gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1214
1215     for (i = 1; i < g_strv_length(cmd); i++)
1216         g_array_prepend_val(argv, cmd[i]);
1217
1218     if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1219     g_free (spacer);
1220     g_strfreev (cmd);
1221 }
1222
1223 static void
1224 spawn_sh_sync(WebKitWebView *web_view, GArray *argv) {
1225     (void)web_view;
1226     if (!uzbl.behave.shell_cmd) {
1227         g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1228         return;
1229     }
1230     
1231     guint i;
1232     gchar *spacer = g_strdup("");
1233     g_array_insert_val(argv, 1, spacer);
1234     gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1235
1236     for (i = 1; i < g_strv_length(cmd); i++)
1237         g_array_prepend_val(argv, cmd[i]);
1238          
1239     if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1240                          TRUE, &uzbl.comm.sync_stdout);
1241     g_free (spacer);
1242     g_strfreev (cmd);
1243 }
1244
1245 static void
1246 parse_command(const char *cmd, const char *param) {
1247     Command *c;
1248
1249     if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1250
1251             guint i;
1252             gchar **par = split_quoted(param, TRUE);
1253             GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1254
1255             if (c[1] == NOSPLIT) { /* don't split */
1256                 sharg_append(a, param);
1257             } else if (par) {
1258                 for (i = 0; i < g_strv_length(par); i++)
1259                     sharg_append(a, par[i]);
1260             }
1261             c[0](uzbl.gui.web_view, a);
1262             g_strfreev (par);
1263             g_array_free (a, TRUE);
1264
1265     } else
1266         g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1267 }
1268
1269 /* command parser */
1270 static void
1271 setup_regex() {
1272     uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
1273             G_REGEX_OPTIMIZE, 0, NULL);
1274 }
1275
1276 static gboolean
1277 get_var_value(const gchar *name) {
1278     uzbl_cmdprop *c;
1279
1280     if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1281         if(c->type == TYPE_STR)
1282             printf("VAR: %s VALUE: (%s)\n", name, (char *)*c->ptr);
1283         else if(c->type == TYPE_INT)
1284             printf("VAR: %s VALUE: %d\n", name, (int)*c->ptr);
1285     }
1286     return TRUE;
1287 }
1288
1289 static void
1290 set_proxy_url() {
1291     SoupURI *suri;
1292
1293     if(*uzbl.net.proxy_url == ' '
1294        || uzbl.net.proxy_url == NULL) {
1295         soup_session_remove_feature_by_type(uzbl.net.soup_session,
1296                 (GType) SOUP_SESSION_PROXY_URI);
1297     }
1298     else {
1299         suri = soup_uri_new(uzbl.net.proxy_url);
1300         g_object_set(G_OBJECT(uzbl.net.soup_session),
1301                 SOUP_SESSION_PROXY_URI,
1302                 suri, NULL);
1303         soup_uri_free(suri);
1304     }
1305     return;
1306 }
1307
1308 static void
1309 cmd_load_uri() {
1310     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1311     g_array_append_val (a, uzbl.state.uri);
1312     load_uri(uzbl.gui.web_view, a);
1313     g_array_free (a, TRUE);
1314 }
1315
1316 static void 
1317 cmd_always_insert_mode() {
1318     uzbl.behave.insert_mode =
1319         uzbl.behave.always_insert_mode ?  TRUE : FALSE;
1320     update_title();
1321 }
1322
1323 static void
1324 cmd_max_conns() {
1325     g_object_set(G_OBJECT(uzbl.net.soup_session),
1326             SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1327 }
1328
1329 static void
1330 cmd_max_conns_host() {
1331     g_object_set(G_OBJECT(uzbl.net.soup_session),
1332             SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1333 }
1334
1335 static void
1336 cmd_http_debug() {
1337     soup_session_remove_feature
1338         (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1339     /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1340     /*g_free(uzbl.net.soup_logger);*/
1341
1342     uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1343     soup_session_add_feature(uzbl.net.soup_session,
1344             SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1345 }
1346
1347 static WebKitWebSettings*
1348 view_settings() {
1349     return webkit_web_view_get_settings(uzbl.gui.web_view);
1350 }
1351
1352 static void
1353 cmd_font_size() {
1354     WebKitWebSettings *ws = view_settings();
1355     if (uzbl.behave.font_size > 0) {
1356         g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1357     }
1358     
1359     if (uzbl.behave.monospace_size > 0) {
1360         g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1361                       uzbl.behave.monospace_size, NULL);
1362     } else {
1363         g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1364                       uzbl.behave.font_size, NULL);
1365     }
1366 }
1367
1368 static void
1369 cmd_disable_plugins() {
1370     g_object_set (G_OBJECT(view_settings()), "enable-plugins", 
1371             !uzbl.behave.disable_plugins, NULL);
1372 }
1373
1374 static void
1375 cmd_disable_scripts() {
1376     g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1377             !uzbl.behave.disable_scripts, NULL);
1378 }
1379
1380 static void
1381 cmd_minimum_font_size() {
1382     g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1383             uzbl.behave.minimum_font_size, NULL);
1384 }
1385 static void
1386 cmd_autoload_img() {
1387     g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1388             uzbl.behave.autoload_img, NULL);
1389 }
1390
1391
1392 static void
1393 cmd_autoshrink_img() {
1394     g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1395             uzbl.behave.autoshrink_img, NULL);
1396 }
1397
1398
1399 static void
1400 cmd_enable_spellcheck() {
1401     g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1402             uzbl.behave.enable_spellcheck, NULL);
1403 }
1404
1405 static void
1406 cmd_enable_private() {
1407     g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1408             uzbl.behave.enable_private, NULL);
1409 }
1410
1411 static void
1412 cmd_print_bg() {
1413     g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1414             uzbl.behave.print_bg, NULL);
1415 }
1416
1417 static void 
1418 cmd_style_uri() {
1419     g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1420             uzbl.behave.style_uri, NULL);
1421 }
1422
1423 static void 
1424 cmd_resizable_txt() {
1425     g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1426             uzbl.behave.resizable_txt, NULL);
1427 }
1428
1429 static void 
1430 cmd_default_encoding() {
1431     g_object_set (G_OBJECT(view_settings()), "default-encoding",
1432             uzbl.behave.default_encoding, NULL);
1433 }
1434
1435 static void 
1436 cmd_enforce_96dpi() {
1437     g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1438             uzbl.behave.enforce_96dpi, NULL);
1439 }
1440
1441 static void 
1442 cmd_caret_browsing() {
1443     g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1444             uzbl.behave.caret_browsing, NULL);
1445 }
1446
1447 static void
1448 cmd_cookie_handler() {
1449     gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1450     /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1451     if ((g_strcmp0(split[0], "sh") == 0) ||
1452         (g_strcmp0(split[0], "spawn") == 0)) {
1453         g_free (uzbl.behave.cookie_handler);
1454         uzbl.behave.cookie_handler =
1455             g_strdup_printf("sync_%s %s", split[0], split[1]);
1456     }
1457     g_strfreev (split);
1458 }
1459
1460 static void
1461 cmd_fifo_dir() {
1462     uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1463 }
1464
1465 static void
1466 cmd_socket_dir() {
1467     uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1468 }
1469
1470 static void
1471 cmd_inject_html() {
1472     if(uzbl.behave.inject_html) {
1473         webkit_web_view_load_html_string (uzbl.gui.web_view,
1474                 uzbl.behave.inject_html, NULL);
1475     }
1476 }
1477
1478 static void
1479 cmd_modkey() {
1480     int i;
1481     char *buf;
1482
1483     buf = g_utf8_strup(uzbl.behave.modkey, -1);
1484     uzbl.behave.modmask = 0;
1485
1486     if(uzbl.behave.modkey) 
1487         g_free(uzbl.behave.modkey);
1488     uzbl.behave.modkey = buf;
1489
1490     for (i = 0; modkeys[i].key != NULL; i++) {
1491         if (g_strrstr(buf, modkeys[i].key))
1492             uzbl.behave.modmask |= modkeys[i].mask;
1493     }
1494 }
1495
1496 static void
1497 cmd_useragent() {
1498     if (*uzbl.net.useragent == ' ') {
1499         g_free (uzbl.net.useragent);
1500         uzbl.net.useragent = NULL;
1501     } else {
1502         gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1503         if (ua)
1504             g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1505         g_free(uzbl.net.useragent);
1506         uzbl.net.useragent = ua;
1507     }
1508 }
1509
1510 static void
1511 move_statusbar() {
1512     gtk_widget_ref(uzbl.gui.scrolled_win);
1513     gtk_widget_ref(uzbl.gui.mainbar);
1514     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1515     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1516
1517     if(uzbl.behave.status_top) {
1518         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1519         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1520     }
1521     else {
1522         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1523         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1524     }
1525     gtk_widget_unref(uzbl.gui.scrolled_win);
1526     gtk_widget_unref(uzbl.gui.mainbar);
1527     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1528     return;
1529 }
1530
1531 static gboolean
1532 set_var_value(gchar *name, gchar *val) {
1533     uzbl_cmdprop *c = NULL;
1534     char *endp = NULL;
1535     char *buf = NULL;
1536
1537     if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1538         /* check for the variable type */
1539         if (c->type == TYPE_STR) {
1540             buf = expand_vars(val);
1541             g_free(*c->ptr);
1542             *c->ptr = buf;
1543         } else if(c->type == TYPE_INT) {
1544             int *ip = (int *)c->ptr;
1545             buf = expand_vars(val);
1546             *ip = (int)strtoul(buf, &endp, 10);
1547             g_free(buf);
1548         }
1549
1550         /* invoke a command specific function */
1551         if(c->func) c->func();
1552     }
1553     return TRUE;
1554 }
1555
1556 static void
1557 render_html() {
1558     Behaviour *b = &uzbl.behave;
1559
1560     if(b->html_buffer->str) {
1561         webkit_web_view_load_html_string (uzbl.gui.web_view,
1562                 b->html_buffer->str, b->base_url);
1563         g_string_free(b->html_buffer, TRUE);
1564         b->html_buffer = g_string_new("");
1565     }
1566 }
1567
1568 enum {M_CMD, M_HTML};
1569 static void
1570 parse_cmd_line(const char *ctl_line) {
1571     gchar **tokens = NULL;
1572     Behaviour *b = &uzbl.behave;
1573     size_t len=0;
1574
1575     if(b->mode == M_HTML) {
1576         len = strlen(b->html_endmarker);
1577         /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1578         if(len == strlen(ctl_line)-1 &&
1579            !strncmp(b->html_endmarker, ctl_line, len)) {
1580             set_timeout(0);
1581             set_var_value("mode", "0");
1582             render_html();
1583             return;
1584         }
1585         else {
1586             set_timeout(b->html_timeout);
1587             g_string_append(b->html_buffer, ctl_line);
1588         }
1589     }
1590     else {
1591         /* ACT command */
1592         if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1593             tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1594             if(tokens[0][0] == 0) {
1595                 parse_command(tokens[1], tokens[2]);
1596             }
1597             else
1598                 printf("Error in command: %s\n", tokens[0]);
1599         }
1600         /* Comments */
1601         else if(   (ctl_line[0] == '#')
1602                 || (ctl_line[0] == ' ')
1603                 || (ctl_line[0] == '\n'))
1604             ; /* ignore these lines */
1605         else {
1606             gchar *ctlstrip;
1607             if (ctl_line[strlen(ctl_line) - 1] == '\n')
1608                 ctlstrip = g_strndup(ctl_line, strlen(ctl_line) - 1);
1609             else ctlstrip = g_strdup(ctl_line);
1610             tokens = g_strsplit(ctlstrip, " ", 2);
1611
1612             parse_command(tokens[0], tokens[1]);
1613             g_free(ctlstrip);
1614         }
1615         if(tokens)
1616             g_strfreev(tokens);
1617     }
1618
1619     return;
1620 }
1621
1622 static gchar*
1623 build_stream_name(int type, const gchar* dir) {
1624     char *xwin_str;
1625     State *s = &uzbl.state;
1626     gchar *str;
1627
1628     xwin_str = itos((int)uzbl.xwin);
1629     if (type == FIFO) {
1630         str = g_strdup_printf
1631             ("%s/uzbl_fifo_%s", dir,
1632              s->instance_name ? s->instance_name : xwin_str);
1633     } else if (type == SOCKET) {
1634         str = g_strdup_printf
1635             ("%s/uzbl_socket_%s", dir,
1636              s->instance_name ? s->instance_name : xwin_str );
1637     }
1638     g_free(xwin_str);
1639     return str;
1640 }
1641
1642 static gboolean
1643 control_fifo(GIOChannel *gio, GIOCondition condition) {
1644     if (uzbl.state.verbose)
1645         printf("triggered\n");
1646     gchar *ctl_line;
1647     GIOStatus ret;
1648     GError *err = NULL;
1649
1650     if (condition & G_IO_HUP)
1651         g_error ("Fifo: Read end of pipe died!\n");
1652
1653     if(!gio)
1654        g_error ("Fifo: GIOChannel broke\n");
1655
1656     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1657     if (ret == G_IO_STATUS_ERROR) {
1658         g_error ("Fifo: Error reading: %s\n", err->message);
1659         g_error_free (err);
1660     }
1661
1662     parse_cmd_line(ctl_line);
1663     g_free(ctl_line);
1664
1665     return TRUE;
1666 }
1667
1668 static gchar*
1669 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1670     if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1671         if (unlink(uzbl.comm.fifo_path) == -1)
1672             g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1673         g_free(uzbl.comm.fifo_path);
1674         uzbl.comm.fifo_path = NULL;
1675     }
1676
1677     if (*dir == ' ') { /* space unsets the variable */
1678         g_free (dir);
1679         return NULL;
1680     }
1681
1682     GIOChannel *chan = NULL;
1683     GError *error = NULL;
1684     gchar *path = build_stream_name(FIFO, dir);
1685
1686     if (!file_exists(path)) {
1687         if (mkfifo (path, 0666) == 0) {
1688             // 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.
1689             chan = g_io_channel_new_file(path, "r+", &error);
1690             if (chan) {
1691                 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1692                     if (uzbl.state.verbose)
1693                         printf ("init_fifo: created successfully as %s\n", path);
1694                     uzbl.comm.fifo_path = path;
1695                     return dir;
1696                 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1697             } else g_warning ("init_fifo: can't open: %s\n", error->message);
1698         } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1699     } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1700
1701     /* if we got this far, there was an error; cleanup */
1702     if (error) g_error_free (error);
1703     g_free(dir);
1704     g_free(path);
1705     return NULL;
1706 }
1707
1708 static gboolean
1709 control_stdin(GIOChannel *gio, GIOCondition condition) {
1710     (void) condition;
1711     gchar *ctl_line = NULL;
1712     GIOStatus ret;
1713
1714     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1715     if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1716         return FALSE;
1717
1718     parse_cmd_line(ctl_line);
1719     g_free(ctl_line);
1720
1721     return TRUE;
1722 }
1723
1724 static void
1725 create_stdin () {
1726     GIOChannel *chan = NULL;
1727     GError *error = NULL;
1728
1729     chan = g_io_channel_unix_new(fileno(stdin));
1730     if (chan) {
1731         if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1732             g_error ("Stdin: could not add watch\n");
1733         } else {
1734             if (uzbl.state.verbose)
1735                 printf ("Stdin: watch added successfully\n");
1736         }
1737     } else {
1738         g_error ("Stdin: Error while opening: %s\n", error->message);
1739     }
1740     if (error) g_error_free (error);
1741 }
1742
1743 static gboolean
1744 control_socket(GIOChannel *chan) {
1745     struct sockaddr_un remote;
1746     char buffer[512], *ctl_line;
1747     char temp[128];
1748     int sock, clientsock, n, done;
1749     unsigned int t;
1750
1751     sock = g_io_channel_unix_get_fd(chan);
1752
1753     memset (buffer, 0, sizeof (buffer));
1754
1755     t          = sizeof (remote);
1756     clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1757
1758     done = 0;
1759     do {
1760         memset (temp, 0, sizeof (temp));
1761         n = recv (clientsock, temp, 128, 0);
1762         if (n == 0) {
1763             buffer[strlen (buffer)] = '\0';
1764             done = 1;
1765         }
1766         if (!done)
1767             strcat (buffer, temp);
1768     } while (!done);
1769
1770     if (strcmp (buffer, "\n") < 0) {
1771         buffer[strlen (buffer) - 1] = '\0';
1772     } else {
1773         buffer[strlen (buffer)] = '\0';
1774     }
1775     close (clientsock);
1776     ctl_line = g_strdup(buffer);
1777     parse_cmd_line (ctl_line);
1778
1779 /*
1780    TODO: we should be able to do it with this.  but glib errors out with "Invalid argument"
1781     GError *error = NULL;
1782     gsize len;
1783     GIOStatus ret;
1784     ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1785     if (ret == G_IO_STATUS_ERROR)
1786         g_error ("Error reading: %s\n", error->message);
1787
1788     printf("Got line %s (%u bytes) \n",ctl_line, len);
1789     if(ctl_line) {
1790        parse_line(ctl_line);
1791 */
1792
1793     g_free(ctl_line);
1794     return TRUE;
1795 }
1796
1797 static gchar*
1798 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1799     if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1800         if (unlink(uzbl.comm.socket_path) == -1)
1801             g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1802         g_free(uzbl.comm.socket_path);
1803         uzbl.comm.socket_path = NULL;
1804     }
1805
1806     if (*dir == ' ') {
1807         g_free(dir);
1808         return NULL;
1809     }
1810
1811     GIOChannel *chan = NULL;
1812     int sock, len;
1813     struct sockaddr_un local;
1814     gchar *path = build_stream_name(SOCKET, dir);
1815
1816     sock = socket (AF_UNIX, SOCK_STREAM, 0);
1817
1818     local.sun_family = AF_UNIX;
1819     strcpy (local.sun_path, path);
1820     unlink (local.sun_path);
1821
1822     len = strlen (local.sun_path) + sizeof (local.sun_family);
1823     if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1824         if (uzbl.state.verbose)
1825             printf ("init_socket: opened in %s\n", path);
1826         listen (sock, 5);
1827
1828         if( (chan = g_io_channel_unix_new(sock)) ) {
1829             g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1830             uzbl.comm.socket_path = path;
1831             return dir;
1832         }
1833     } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1834
1835     /* if we got this far, there was an error; cleanup */
1836     g_free(path);
1837     g_free(dir);
1838     return NULL;
1839 }
1840
1841 /*
1842  NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1843  it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1844 */
1845 // this function may be called very early when the templates are not set (yet), hence the checks
1846 static void
1847 update_title (void) {
1848     Behaviour *b = &uzbl.behave;
1849     gchar *parsed;
1850
1851     if (b->show_status) {
1852         if (b->title_format_short) {
1853             parsed = expand_template(b->title_format_short, FALSE);
1854             gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1855             g_free(parsed);
1856         }
1857         if (b->status_format) {
1858             parsed = expand_template(b->status_format, TRUE);
1859             gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1860             g_free(parsed);
1861         }
1862         if (b->status_background) {
1863             GdkColor color;
1864             gdk_color_parse (b->status_background, &color);
1865             //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)
1866             gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1867         }
1868     } else {
1869         if (b->title_format_long) {
1870             parsed = expand_template(b->title_format_long, FALSE);
1871             gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1872             g_free(parsed);
1873         }
1874     }
1875 }
1876
1877 static gboolean
1878 key_press_cb (GtkWidget* window, GdkEventKey* event)
1879 {
1880     //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1881
1882     (void) window;
1883
1884     if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1885         || 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)
1886         return FALSE;
1887
1888     /* turn off insert mode (if always_insert_mode is not used) */
1889     if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1890         uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1891         update_title();
1892         return TRUE;
1893     }
1894
1895     if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1896         return FALSE;
1897
1898     if (event->keyval == GDK_Escape) {
1899         g_string_truncate(uzbl.state.keycmd, 0);
1900         update_title();
1901         dehilight(uzbl.gui.web_view, NULL);
1902         return TRUE;
1903     }
1904
1905     //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1906     if (event->keyval == GDK_Insert) {
1907         gchar * str;
1908         if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1909             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1910         } else {
1911             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1912         }
1913         if (str) {
1914             g_string_append (uzbl.state.keycmd, str);
1915             update_title ();
1916             g_free (str);
1917         }
1918         return TRUE;
1919     }
1920
1921     if (event->keyval == GDK_BackSpace)
1922         keycmd_bs(NULL, NULL);
1923
1924     gboolean key_ret = FALSE;
1925     if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1926         key_ret = TRUE;
1927     if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1928
1929     run_keycmd(key_ret);
1930     update_title();
1931     if (key_ret) return (!uzbl.behave.insert_mode);
1932     return TRUE;
1933 }
1934
1935 static void
1936 run_keycmd(const gboolean key_ret) {
1937     /* run the keycmd immediately if it isn't incremental and doesn't take args */
1938     Action *act;
1939     if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1940         g_string_truncate(uzbl.state.keycmd, 0);
1941         parse_command(act->name, act->param);
1942         return;
1943     }
1944
1945     /* try if it's an incremental keycmd or one that takes args, and run it */
1946     GString* short_keys = g_string_new ("");
1947     GString* short_keys_inc = g_string_new ("");
1948     guint i;
1949     for (i=0; i<(uzbl.state.keycmd->len); i++) {
1950         g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1951         g_string_assign(short_keys_inc, short_keys->str);
1952         g_string_append_c(short_keys, '_');
1953         g_string_append_c(short_keys_inc, '*');
1954
1955         if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1956             /* run normal cmds only if return was pressed */
1957             exec_paramcmd(act, i);
1958             g_string_truncate(uzbl.state.keycmd, 0);
1959             break;
1960         } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1961             if (key_ret)  /* just quit the incremental command on return */
1962                 g_string_truncate(uzbl.state.keycmd, 0);
1963             else exec_paramcmd(act, i); /* otherwise execute the incremental */
1964             break;
1965         }
1966         
1967         g_string_truncate(short_keys, short_keys->len - 1);
1968     }
1969     g_string_free (short_keys, TRUE);
1970     g_string_free (short_keys_inc, TRUE);
1971 }
1972
1973 static void
1974 exec_paramcmd(const Action *act, const guint i) {
1975     GString *parampart = g_string_new (uzbl.state.keycmd->str);
1976     GString *actionname = g_string_new ("");
1977     GString *actionparam = g_string_new ("");
1978     g_string_erase (parampart, 0, i+1);
1979     if (act->name)
1980         g_string_printf (actionname, act->name, parampart->str);
1981     if (act->param)
1982         g_string_printf (actionparam, act->param, parampart->str);
1983     parse_command(actionname->str, actionparam->str);
1984     g_string_free(actionname, TRUE);
1985     g_string_free(actionparam, TRUE);
1986     g_string_free(parampart, TRUE);
1987 }
1988
1989
1990 static GtkWidget*
1991 create_browser () {
1992     GUI *g = &uzbl.gui;
1993
1994     GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1995     //main_window_ref = g_object_ref(scrolled_window);
1996     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
1997
1998     g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1999     gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2000
2001     g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
2002     g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2003     g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2004     g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2005     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2006     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2007     g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2008     g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2009     g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2010     g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2011
2012     return scrolled_window;
2013 }
2014
2015 static GtkWidget*
2016 create_mainbar () {
2017     GUI *g = &uzbl.gui;
2018
2019     g->mainbar = gtk_hbox_new (FALSE, 0);
2020
2021     /* keep a reference to the bar so we can re-pack it at runtime*/
2022     //sbar_ref = g_object_ref(g->mainbar);
2023
2024     g->mainbar_label = gtk_label_new ("");
2025     gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2026     gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2027     gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2028     gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2029     gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2030     return g->mainbar;
2031 }
2032
2033 static
2034 GtkWidget* create_window () {
2035     GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2036     gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2037     gtk_widget_set_name (window, "Uzbl browser");
2038     g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2039     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2040
2041     return window;
2042 }
2043
2044 static gchar**
2045 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2046     /*
2047       If actname is one that calls an external command, this function will inject
2048       newargs in front of the user-provided args in that command line.  They will
2049       come become after the body of the script (in sh) or after the name of
2050       the command to execute (in spawn).
2051       i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2052       span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2053
2054       The return value consist of two strings: the action (sh, ...) and its args.
2055
2056       If act is not one that calls an external command, then the given action merely
2057       gets duplicated.
2058     */
2059     GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2060     gchar *actdup = g_strdup(actname);
2061     g_array_append_val(rets, actdup);
2062
2063     if ((g_strcmp0(actname, "spawn") == 0) ||
2064         (g_strcmp0(actname, "sh") == 0) ||
2065         (g_strcmp0(actname, "sync_spawn") == 0) ||
2066         (g_strcmp0(actname, "sync_sh") == 0)) {
2067         guint i;
2068         GString *a = g_string_new("");
2069         gchar **spawnparts = split_quoted(origargs, FALSE);
2070         g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2071         if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2072
2073         for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2074             if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2075
2076         g_array_append_val(rets, a->str);
2077         g_string_free(a, FALSE);
2078         g_strfreev(spawnparts);
2079     } else {
2080         gchar *origdup = g_strdup(origargs);
2081         g_array_append_val(rets, origdup);
2082     }
2083     return (gchar**)g_array_free(rets, FALSE);
2084 }
2085
2086 static void
2087 run_handler (const gchar *act, const gchar *args) {
2088     /* Consider this code a temporary hack to make the handlers usable.
2089        In practice, all this splicing, injection, and reconstruction is
2090        inefficient, annoying and hard to manage.  Potential pitfalls arise
2091        when the handler specific args 1) are not quoted  (the handler
2092        callbacks should take care of this)  2) are quoted but interfere
2093        with the users' own quotation.  A more ideal solution is
2094        to refactor parse_command so that it doesn't just take a string
2095        and execute it; rather than that, we should have a function which
2096        returns the argument vector parsed from the string.  This vector
2097        could be modified (e.g. insert additional args into it) before
2098        passing it to the next function that actually executes it.  Though
2099        it still isn't perfect for chain actions..  will reconsider & re-
2100        factor when I have the time. -duc */
2101
2102     char **parts = g_strsplit(act, " ", 2);
2103     if (!parts) return;
2104     if (g_strcmp0(parts[0], "chain") == 0) {
2105         GString *newargs = g_string_new("");
2106         gchar **chainparts = split_quoted(parts[1], FALSE);
2107         
2108         /* for every argument in the chain, inject the handler args
2109            and make sure the new parts are wrapped in quotes */
2110         gchar **cp = chainparts;
2111         gchar quot = '\'';
2112         gchar *quotless = NULL;
2113         gchar **spliced_quotless = NULL; // sigh -_-;
2114         gchar **inpart = NULL;
2115         
2116         while (*cp) {
2117             if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2118                 quot = **cp;
2119                 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2120             } else quotless = g_strdup(*cp);
2121
2122             spliced_quotless = g_strsplit(quotless, " ", 2);
2123             inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2124             g_strfreev(spliced_quotless);
2125             
2126             g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2127             g_free(quotless);
2128             g_strfreev(inpart);
2129             cp++;
2130         }
2131
2132         parse_command(parts[0], &(newargs->str[1]));
2133         g_string_free(newargs, TRUE);
2134         g_strfreev(chainparts);
2135         
2136     } else {
2137         gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2138         parse_command(inparts[0], inparts[1]);
2139         g_free(inparts[0]);
2140         g_free(inparts[1]);
2141     }
2142     g_strfreev(parts);
2143 }
2144
2145 static void
2146 add_binding (const gchar *key, const gchar *act) {
2147     char **parts = g_strsplit(act, " ", 2);
2148     Action *action;
2149
2150     if (!parts)
2151         return;
2152
2153     //Debug:
2154     if (uzbl.state.verbose)
2155         printf ("Binding %-10s : %s\n", key, act);
2156     action = new_action(parts[0], parts[1]);
2157
2158     g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2159     g_strfreev(parts);
2160 }
2161
2162 static gchar*
2163 get_xdg_var (XDG_Var xdg) {
2164     const gchar* actual_value = getenv (xdg.environmental);
2165     const gchar* home         = getenv ("HOME");
2166
2167     gchar* return_value = str_replace ("~", home, actual_value);
2168
2169     if (! actual_value || strcmp (actual_value, "") == 0) {
2170         if (xdg.default_value) {
2171             return_value = str_replace ("~", home, xdg.default_value);
2172         } else {
2173             return_value = NULL;
2174         }
2175     }
2176     return return_value;
2177 }
2178
2179 static gchar*
2180 find_xdg_file (int xdg_type, char* filename) {
2181     /* xdg_type = 0 => config
2182        xdg_type = 1 => data
2183        xdg_type = 2 => cache*/
2184
2185     gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2186     gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2187     g_free (xdgv);
2188
2189     gchar* temporary_string;
2190     char*  saveptr;
2191     char*  buf;
2192
2193     if (! file_exists (temporary_file) && xdg_type != 2) {
2194         buf = get_xdg_var (XDG[3 + xdg_type]);
2195         temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2196         g_free(buf);
2197
2198         while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2199             g_free (temporary_file);
2200             temporary_file = g_strconcat (temporary_string, filename, NULL);
2201         }
2202     }
2203     
2204     //g_free (temporary_string); - segfaults.
2205
2206     if (file_exists (temporary_file)) {
2207         return temporary_file;
2208     } else {
2209         return NULL;
2210     }
2211 }
2212 static void
2213 settings_init () {
2214     State *s = &uzbl.state;
2215     Network *n = &uzbl.net;
2216     int i;
2217     for (i = 0; default_config[i].command != NULL; i++) {
2218         parse_cmd_line(default_config[i].command);
2219     }
2220
2221     if (!s->config_file) {
2222         s->config_file = find_xdg_file (0, "/uzbl/config");
2223     }
2224
2225     if (s->config_file) {
2226         GArray* lines = read_file_by_line (s->config_file);
2227         int i = 0;
2228         gchar* line;
2229
2230         while ((line = g_array_index(lines, gchar*, i))) {
2231             parse_cmd_line (line);
2232             i ++;
2233             g_free (line);
2234         }
2235         g_array_free (lines, TRUE);
2236     } else {
2237         if (uzbl.state.verbose)
2238             printf ("No configuration file loaded.\n");
2239     }
2240
2241     g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
2242 }
2243
2244 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2245     (void) session;
2246     (void) user_data;
2247     if (!uzbl.behave.cookie_handler) return;
2248
2249     soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2250     GString *s = g_string_new ("");
2251     SoupURI * soup_uri = soup_message_get_uri(msg);
2252     g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2253     run_handler(uzbl.behave.cookie_handler, s->str);
2254
2255     if(uzbl.comm.sync_stdout)
2256         soup_message_headers_replace (msg->request_headers, "Cookie", uzbl.comm.sync_stdout);
2257     //printf("stdout: %s\n", uzbl.comm.sync_stdout);   // debugging
2258     if (uzbl.comm.sync_stdout) uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2259         
2260     g_string_free(s, TRUE);
2261 }
2262
2263 static void
2264 save_cookies (SoupMessage *msg, gpointer user_data){
2265     (void) user_data;
2266     GSList *ck;
2267     char *cookie;
2268     for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2269         cookie = soup_cookie_to_set_cookie_header(ck->data);
2270         SoupURI * soup_uri = soup_message_get_uri(msg);
2271         GString *s = g_string_new ("");
2272         g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2273         run_handler(uzbl.behave.cookie_handler, s->str);
2274         g_free (cookie);
2275         g_string_free(s, TRUE);
2276     }
2277     g_slist_free(ck);
2278 }
2279
2280 /* --- WEBINSPECTOR --- */
2281 static void
2282 hide_window_cb(GtkWidget *widget, gpointer data) {
2283     (void) data;
2284
2285     gtk_widget_hide(widget);
2286 }
2287
2288 static WebKitWebView*
2289 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2290     (void) data;
2291     (void) page;
2292     (void) web_inspector;
2293     GtkWidget* scrolled_window;
2294     GtkWidget* new_web_view;
2295     GUI *g = &uzbl.gui;
2296
2297     g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2298     g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2299             G_CALLBACK(hide_window_cb), NULL);
2300
2301     gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2302     gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2303     gtk_widget_show(g->inspector_window);
2304
2305     scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2306     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2307             GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2308     gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2309     gtk_widget_show(scrolled_window);
2310
2311     new_web_view = webkit_web_view_new();
2312     gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2313
2314     return WEBKIT_WEB_VIEW(new_web_view);
2315 }
2316
2317 static gboolean
2318 inspector_show_window_cb (WebKitWebInspector* inspector){
2319     (void) inspector;
2320     gtk_widget_show(uzbl.gui.inspector_window);
2321     return TRUE;
2322 }
2323
2324 /* TODO: Add variables and code to make use of these functions */
2325 static gboolean
2326 inspector_close_window_cb (WebKitWebInspector* inspector){
2327     (void) inspector;
2328     return TRUE;
2329 }
2330
2331 static gboolean
2332 inspector_attach_window_cb (WebKitWebInspector* inspector){
2333     (void) inspector;
2334     return FALSE;
2335 }
2336
2337 static gboolean
2338 inspector_dettach_window_cb (WebKitWebInspector* inspector){
2339     (void) inspector;
2340     return FALSE;
2341 }
2342
2343 static gboolean
2344 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2345     (void) inspector;
2346     return FALSE;
2347 }
2348
2349 static gboolean
2350 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2351     (void) inspector;
2352     return FALSE;
2353 }
2354
2355 static void
2356 set_up_inspector() {
2357     GUI *g = &uzbl.gui;
2358     WebKitWebSettings *settings = view_settings();
2359     g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2360
2361     uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2362     g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2363     g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2364     g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2365     g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2366     g_signal_connect (G_OBJECT (g->inspector), "dettach-window", G_CALLBACK (inspector_dettach_window_cb), NULL);
2367     g_signal_connect (G_OBJECT (g->inspector), "destroy", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2368
2369     g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2370 }
2371
2372 static void
2373 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2374     (void) ud;
2375     uzbl_cmdprop *c = v;
2376
2377     if(!c->dump)
2378         return;
2379
2380     if(c->type == TYPE_STR)
2381         printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2382     else if(c->type == TYPE_INT)
2383         printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2384 }
2385
2386 static void
2387 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2388     (void) ud;
2389     Action *a = v;
2390
2391     printf("bind %s = %s %s\n", (char *)k ,
2392             (char *)a->name, a->param?(char *)a->param:"");
2393 }
2394
2395 static void
2396 dump_config() {
2397     g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2398     g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2399 }
2400
2401 /** -- MAIN -- **/
2402 int
2403 main (int argc, char* argv[]) {
2404     gtk_init (&argc, &argv);
2405     if (!g_thread_supported ())
2406         g_thread_init (NULL);
2407     uzbl.state.executable_path = g_strdup(argv[0]);
2408     uzbl.state.selected_url = NULL;
2409     uzbl.state.searchtx = NULL;
2410
2411     GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2412     g_option_context_add_main_entries (context, entries, NULL);
2413     g_option_context_add_group (context, gtk_get_option_group (TRUE));
2414     g_option_context_parse (context, &argc, &argv, NULL);
2415     g_option_context_free(context);
2416     /* initialize hash table */
2417     uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2418
2419     uzbl.net.soup_session = webkit_get_default_session();
2420     uzbl.state.keycmd = g_string_new("");
2421
2422     if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2423         fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2424     if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2425         fprintf(stderr, "uzbl: error hooking SIGINT\n");
2426     if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2427         fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2428
2429
2430     if(uname(&uzbl.state.unameinfo) == -1)
2431         g_printerr("Can't retrieve unameinfo.  Your useragent might appear wrong.\n");
2432
2433     uzbl.gui.sbar.progress_s = g_strdup("=");
2434     uzbl.gui.sbar.progress_u = g_strdup("ยท");
2435     uzbl.gui.sbar.progress_w = 10;
2436
2437     /* HTML mode defaults*/
2438     uzbl.behave.html_buffer = g_string_new("");
2439     uzbl.behave.html_endmarker = g_strdup(".");
2440     uzbl.behave.html_timeout = 60;
2441     uzbl.behave.base_url = g_strdup("http://invalid");
2442
2443     /* default mode indicators */
2444     uzbl.behave.insert_indicator = g_strdup("I");
2445     uzbl.behave.cmd_indicator    = g_strdup("C");
2446
2447     setup_regex();
2448     setup_scanner();
2449     commands_hash ();
2450     make_var_to_name_hash();
2451
2452     uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2453
2454     uzbl.gui.scrolled_win = create_browser();
2455     create_mainbar();
2456
2457     /* initial packing */
2458     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2459     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2460
2461     uzbl.gui.main_window = create_window ();
2462     gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2463
2464
2465     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2466     gtk_widget_show_all (uzbl.gui.main_window);
2467     uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2468
2469     if (uzbl.state.verbose) {
2470         printf("Uzbl start location: %s\n", argv[0]);
2471         printf("window_id %i\n",(int) uzbl.xwin);
2472         printf("pid %i\n", getpid ());
2473         printf("name: %s\n", uzbl.state.instance_name);
2474     }
2475
2476     uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2477     uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2478     uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2479     uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2480     gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2481
2482     settings_init ();
2483
2484     if (!uzbl.behave.show_status)
2485         gtk_widget_hide(uzbl.gui.mainbar);
2486     else
2487         update_title();
2488
2489     /* WebInspector */
2490     set_up_inspector();
2491
2492     create_stdin();
2493
2494     if(uzbl.state.uri) {
2495         GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
2496         g_array_append_val(a, uzbl.state.uri);
2497         load_uri (uzbl.gui.web_view, a);
2498         g_array_free (a, TRUE);
2499     }
2500
2501     gtk_main ();
2502     clean_up();
2503
2504     return EXIT_SUCCESS;
2505 }
2506
2507 /* vi: set et ts=4: */