notify user of stuff re: cookies + save_cookie function
[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 static gboolean
1270 get_var_value(const gchar *name) {
1271     uzbl_cmdprop *c;
1272
1273     if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1274         if(c->type == TYPE_STR)
1275             printf("VAR: %s VALUE: (%s)\n", name, (char *)*c->ptr);
1276         else if(c->type == TYPE_INT)
1277             printf("VAR: %s VALUE: %d\n", name, (int)*c->ptr);
1278     }
1279     return TRUE;
1280 }
1281
1282 static void
1283 set_proxy_url() {
1284     SoupURI *suri;
1285
1286     if(*uzbl.net.proxy_url == ' '
1287        || uzbl.net.proxy_url == NULL) {
1288         soup_session_remove_feature_by_type(uzbl.net.soup_session,
1289                 (GType) SOUP_SESSION_PROXY_URI);
1290     }
1291     else {
1292         suri = soup_uri_new(uzbl.net.proxy_url);
1293         g_object_set(G_OBJECT(uzbl.net.soup_session),
1294                 SOUP_SESSION_PROXY_URI,
1295                 suri, NULL);
1296         soup_uri_free(suri);
1297     }
1298     return;
1299 }
1300
1301 static void
1302 cmd_load_uri() {
1303     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1304     g_array_append_val (a, uzbl.state.uri);
1305     load_uri(uzbl.gui.web_view, a);
1306     g_array_free (a, TRUE);
1307 }
1308
1309 static void 
1310 cmd_always_insert_mode() {
1311     uzbl.behave.insert_mode =
1312         uzbl.behave.always_insert_mode ?  TRUE : FALSE;
1313     update_title();
1314 }
1315
1316 static void
1317 cmd_max_conns() {
1318     g_object_set(G_OBJECT(uzbl.net.soup_session),
1319             SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1320 }
1321
1322 static void
1323 cmd_max_conns_host() {
1324     g_object_set(G_OBJECT(uzbl.net.soup_session),
1325             SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1326 }
1327
1328 static void
1329 cmd_http_debug() {
1330     soup_session_remove_feature
1331         (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1332     /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1333     /*g_free(uzbl.net.soup_logger);*/
1334
1335     uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1336     soup_session_add_feature(uzbl.net.soup_session,
1337             SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1338 }
1339
1340 static WebKitWebSettings*
1341 view_settings() {
1342     return webkit_web_view_get_settings(uzbl.gui.web_view);
1343 }
1344
1345 static void
1346 cmd_font_size() {
1347     WebKitWebSettings *ws = view_settings();
1348     if (uzbl.behave.font_size > 0) {
1349         g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1350     }
1351     
1352     if (uzbl.behave.monospace_size > 0) {
1353         g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1354                       uzbl.behave.monospace_size, NULL);
1355     } else {
1356         g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1357                       uzbl.behave.font_size, NULL);
1358     }
1359 }
1360
1361 static void
1362 cmd_disable_plugins() {
1363     g_object_set (G_OBJECT(view_settings()), "enable-plugins", 
1364             !uzbl.behave.disable_plugins, NULL);
1365 }
1366
1367 static void
1368 cmd_disable_scripts() {
1369     g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1370             !uzbl.behave.disable_scripts, NULL);
1371 }
1372
1373 static void
1374 cmd_minimum_font_size() {
1375     g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1376             uzbl.behave.minimum_font_size, NULL);
1377 }
1378 static void
1379 cmd_autoload_img() {
1380     g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1381             uzbl.behave.autoload_img, NULL);
1382 }
1383
1384
1385 static void
1386 cmd_autoshrink_img() {
1387     g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1388             uzbl.behave.autoshrink_img, NULL);
1389 }
1390
1391
1392 static void
1393 cmd_enable_spellcheck() {
1394     g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1395             uzbl.behave.enable_spellcheck, NULL);
1396 }
1397
1398 static void
1399 cmd_enable_private() {
1400     g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1401             uzbl.behave.enable_private, NULL);
1402 }
1403
1404 static void
1405 cmd_print_bg() {
1406     g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1407             uzbl.behave.print_bg, NULL);
1408 }
1409
1410 static void 
1411 cmd_style_uri() {
1412     g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1413             uzbl.behave.style_uri, NULL);
1414 }
1415
1416 static void 
1417 cmd_resizable_txt() {
1418     g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1419             uzbl.behave.resizable_txt, NULL);
1420 }
1421
1422 static void 
1423 cmd_default_encoding() {
1424     g_object_set (G_OBJECT(view_settings()), "default-encoding",
1425             uzbl.behave.default_encoding, NULL);
1426 }
1427
1428 static void 
1429 cmd_enforce_96dpi() {
1430     g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1431             uzbl.behave.enforce_96dpi, NULL);
1432 }
1433
1434 static void 
1435 cmd_caret_browsing() {
1436     g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1437             uzbl.behave.caret_browsing, NULL);
1438 }
1439
1440 static void
1441 cmd_cookie_handler() {
1442     gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1443     /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1444     if ((g_strcmp0(split[0], "sh") == 0) ||
1445         (g_strcmp0(split[0], "spawn") == 0)) {
1446         g_free (uzbl.behave.cookie_handler);
1447         uzbl.behave.cookie_handler =
1448             g_strdup_printf("sync_%s %s", split[0], split[1]);
1449     }
1450     g_strfreev (split);
1451 }
1452
1453 static void
1454 cmd_fifo_dir() {
1455     uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1456 }
1457
1458 static void
1459 cmd_socket_dir() {
1460     uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1461 }
1462
1463 static void
1464 cmd_inject_html() {
1465     if(uzbl.behave.inject_html) {
1466         webkit_web_view_load_html_string (uzbl.gui.web_view,
1467                 uzbl.behave.inject_html, NULL);
1468     }
1469 }
1470
1471 static void
1472 cmd_modkey() {
1473     int i;
1474     char *buf;
1475
1476     buf = g_utf8_strup(uzbl.behave.modkey, -1);
1477     uzbl.behave.modmask = 0;
1478
1479     if(uzbl.behave.modkey) 
1480         g_free(uzbl.behave.modkey);
1481     uzbl.behave.modkey = buf;
1482
1483     for (i = 0; modkeys[i].key != NULL; i++) {
1484         if (g_strrstr(buf, modkeys[i].key))
1485             uzbl.behave.modmask |= modkeys[i].mask;
1486     }
1487 }
1488
1489 static void
1490 cmd_useragent() {
1491     if (*uzbl.net.useragent == ' ') {
1492         g_free (uzbl.net.useragent);
1493         uzbl.net.useragent = NULL;
1494     } else {
1495         gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1496         if (ua)
1497             g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1498         g_free(uzbl.net.useragent);
1499         uzbl.net.useragent = ua;
1500     }
1501 }
1502
1503 static void
1504 move_statusbar() {
1505     gtk_widget_ref(uzbl.gui.scrolled_win);
1506     gtk_widget_ref(uzbl.gui.mainbar);
1507     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1508     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1509
1510     if(uzbl.behave.status_top) {
1511         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1512         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1513     }
1514     else {
1515         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1516         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1517     }
1518     gtk_widget_unref(uzbl.gui.scrolled_win);
1519     gtk_widget_unref(uzbl.gui.mainbar);
1520     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1521     return;
1522 }
1523
1524 static gboolean
1525 set_var_value(gchar *name, gchar *val) {
1526     uzbl_cmdprop *c = NULL;
1527     char *endp = NULL;
1528     char *buf = NULL;
1529
1530     if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1531         /* check for the variable type */
1532         if (c->type == TYPE_STR) {
1533             buf = expand_vars(val);
1534             g_free(*c->ptr);
1535             *c->ptr = buf;
1536         } else if(c->type == TYPE_INT) {
1537             int *ip = (int *)c->ptr;
1538             buf = expand_vars(val);
1539             *ip = (int)strtoul(buf, &endp, 10);
1540             g_free(buf);
1541         }
1542
1543         /* invoke a command specific function */
1544         if(c->func) c->func();
1545     }
1546     return TRUE;
1547 }
1548
1549 static void
1550 render_html() {
1551     Behaviour *b = &uzbl.behave;
1552
1553     if(b->html_buffer->str) {
1554         webkit_web_view_load_html_string (uzbl.gui.web_view,
1555                 b->html_buffer->str, b->base_url);
1556         g_string_free(b->html_buffer, TRUE);
1557         b->html_buffer = g_string_new("");
1558     }
1559 }
1560
1561 enum {M_CMD, M_HTML};
1562 static void
1563 parse_cmd_line(const char *ctl_line) {
1564     Behaviour *b = &uzbl.behave;
1565     size_t len=0;
1566
1567     if(b->mode == M_HTML) {
1568         len = strlen(b->html_endmarker);
1569         /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1570         if(len == strlen(ctl_line)-1 &&
1571            !strncmp(b->html_endmarker, ctl_line, len)) {
1572             set_timeout(0);
1573             set_var_value("mode", "0");
1574             render_html();
1575             return;
1576         }
1577         else {
1578             set_timeout(b->html_timeout);
1579             g_string_append(b->html_buffer, ctl_line);
1580         }
1581     }
1582     else if((ctl_line[0] == '#') /* Comments */
1583             || (ctl_line[0] == ' ')
1584             || (ctl_line[0] == '\n'))
1585         ; /* ignore these lines */
1586     else { /* parse a command */
1587         gchar *ctlstrip;
1588         gchar **tokens = NULL;
1589
1590         if (ctl_line[strlen(ctl_line) - 1] == '\n') /* strip trailing newline */
1591             ctlstrip = g_strndup(ctl_line, strlen(ctl_line) - 1);
1592         else ctlstrip = g_strdup(ctl_line);
1593
1594         tokens = g_strsplit(ctlstrip, " ", 2);
1595         parse_command(tokens[0], tokens[1]);
1596         g_free(ctlstrip);
1597         g_strfreev(tokens);
1598     }
1599 }
1600
1601 static gchar*
1602 build_stream_name(int type, const gchar* dir) {
1603     char *xwin_str;
1604     State *s = &uzbl.state;
1605     gchar *str;
1606
1607     xwin_str = itos((int)uzbl.xwin);
1608     if (type == FIFO) {
1609         str = g_strdup_printf
1610             ("%s/uzbl_fifo_%s", dir,
1611              s->instance_name ? s->instance_name : xwin_str);
1612     } else if (type == SOCKET) {
1613         str = g_strdup_printf
1614             ("%s/uzbl_socket_%s", dir,
1615              s->instance_name ? s->instance_name : xwin_str );
1616     }
1617     g_free(xwin_str);
1618     return str;
1619 }
1620
1621 static gboolean
1622 control_fifo(GIOChannel *gio, GIOCondition condition) {
1623     if (uzbl.state.verbose)
1624         printf("triggered\n");
1625     gchar *ctl_line;
1626     GIOStatus ret;
1627     GError *err = NULL;
1628
1629     if (condition & G_IO_HUP)
1630         g_error ("Fifo: Read end of pipe died!\n");
1631
1632     if(!gio)
1633        g_error ("Fifo: GIOChannel broke\n");
1634
1635     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1636     if (ret == G_IO_STATUS_ERROR) {
1637         g_error ("Fifo: Error reading: %s\n", err->message);
1638         g_error_free (err);
1639     }
1640
1641     parse_cmd_line(ctl_line);
1642     g_free(ctl_line);
1643
1644     return TRUE;
1645 }
1646
1647 static gchar*
1648 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1649     if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1650         if (unlink(uzbl.comm.fifo_path) == -1)
1651             g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1652         g_free(uzbl.comm.fifo_path);
1653         uzbl.comm.fifo_path = NULL;
1654     }
1655
1656     if (*dir == ' ') { /* space unsets the variable */
1657         g_free (dir);
1658         return NULL;
1659     }
1660
1661     GIOChannel *chan = NULL;
1662     GError *error = NULL;
1663     gchar *path = build_stream_name(FIFO, dir);
1664
1665     if (!file_exists(path)) {
1666         if (mkfifo (path, 0666) == 0) {
1667             // 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.
1668             chan = g_io_channel_new_file(path, "r+", &error);
1669             if (chan) {
1670                 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1671                     if (uzbl.state.verbose)
1672                         printf ("init_fifo: created successfully as %s\n", path);
1673                     uzbl.comm.fifo_path = path;
1674                     return dir;
1675                 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1676             } else g_warning ("init_fifo: can't open: %s\n", error->message);
1677         } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1678     } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1679
1680     /* if we got this far, there was an error; cleanup */
1681     if (error) g_error_free (error);
1682     g_free(dir);
1683     g_free(path);
1684     return NULL;
1685 }
1686
1687 static gboolean
1688 control_stdin(GIOChannel *gio, GIOCondition condition) {
1689     (void) condition;
1690     gchar *ctl_line = NULL;
1691     GIOStatus ret;
1692
1693     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1694     if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1695         return FALSE;
1696
1697     parse_cmd_line(ctl_line);
1698     g_free(ctl_line);
1699
1700     return TRUE;
1701 }
1702
1703 static void
1704 create_stdin () {
1705     GIOChannel *chan = NULL;
1706     GError *error = NULL;
1707
1708     chan = g_io_channel_unix_new(fileno(stdin));
1709     if (chan) {
1710         if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1711             g_error ("Stdin: could not add watch\n");
1712         } else {
1713             if (uzbl.state.verbose)
1714                 printf ("Stdin: watch added successfully\n");
1715         }
1716     } else {
1717         g_error ("Stdin: Error while opening: %s\n", error->message);
1718     }
1719     if (error) g_error_free (error);
1720 }
1721
1722 static gboolean
1723 control_socket(GIOChannel *chan) {
1724     struct sockaddr_un remote;
1725     char buffer[512], *ctl_line;
1726     char temp[128];
1727     int sock, clientsock, n, done;
1728     unsigned int t;
1729
1730     sock = g_io_channel_unix_get_fd(chan);
1731
1732     memset (buffer, 0, sizeof (buffer));
1733
1734     t          = sizeof (remote);
1735     clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1736
1737     done = 0;
1738     do {
1739         memset (temp, 0, sizeof (temp));
1740         n = recv (clientsock, temp, 128, 0);
1741         if (n == 0) {
1742             buffer[strlen (buffer)] = '\0';
1743             done = 1;
1744         }
1745         if (!done)
1746             strcat (buffer, temp);
1747     } while (!done);
1748
1749     if (strcmp (buffer, "\n") < 0) {
1750         buffer[strlen (buffer) - 1] = '\0';
1751     } else {
1752         buffer[strlen (buffer)] = '\0';
1753     }
1754     close (clientsock);
1755     ctl_line = g_strdup(buffer);
1756     parse_cmd_line (ctl_line);
1757
1758 /*
1759    TODO: we should be able to do it with this.  but glib errors out with "Invalid argument"
1760     GError *error = NULL;
1761     gsize len;
1762     GIOStatus ret;
1763     ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1764     if (ret == G_IO_STATUS_ERROR)
1765         g_error ("Error reading: %s\n", error->message);
1766
1767     printf("Got line %s (%u bytes) \n",ctl_line, len);
1768     if(ctl_line) {
1769        parse_line(ctl_line);
1770 */
1771
1772     g_free(ctl_line);
1773     return TRUE;
1774 }
1775
1776 static gchar*
1777 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1778     if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1779         if (unlink(uzbl.comm.socket_path) == -1)
1780             g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1781         g_free(uzbl.comm.socket_path);
1782         uzbl.comm.socket_path = NULL;
1783     }
1784
1785     if (*dir == ' ') {
1786         g_free(dir);
1787         return NULL;
1788     }
1789
1790     GIOChannel *chan = NULL;
1791     int sock, len;
1792     struct sockaddr_un local;
1793     gchar *path = build_stream_name(SOCKET, dir);
1794
1795     sock = socket (AF_UNIX, SOCK_STREAM, 0);
1796
1797     local.sun_family = AF_UNIX;
1798     strcpy (local.sun_path, path);
1799     unlink (local.sun_path);
1800
1801     len = strlen (local.sun_path) + sizeof (local.sun_family);
1802     if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1803         if (uzbl.state.verbose)
1804             printf ("init_socket: opened in %s\n", path);
1805         listen (sock, 5);
1806
1807         if( (chan = g_io_channel_unix_new(sock)) ) {
1808             g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1809             uzbl.comm.socket_path = path;
1810             return dir;
1811         }
1812     } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1813
1814     /* if we got this far, there was an error; cleanup */
1815     g_free(path);
1816     g_free(dir);
1817     return NULL;
1818 }
1819
1820 /*
1821  NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1822  it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1823 */
1824 // this function may be called very early when the templates are not set (yet), hence the checks
1825 static void
1826 update_title (void) {
1827     Behaviour *b = &uzbl.behave;
1828     gchar *parsed;
1829
1830     if (b->show_status) {
1831         if (b->title_format_short) {
1832             parsed = expand_template(b->title_format_short, FALSE);
1833             gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1834             g_free(parsed);
1835         }
1836         if (b->status_format) {
1837             parsed = expand_template(b->status_format, TRUE);
1838             gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1839             g_free(parsed);
1840         }
1841         if (b->status_background) {
1842             GdkColor color;
1843             gdk_color_parse (b->status_background, &color);
1844             //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)
1845             gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1846         }
1847     } else {
1848         if (b->title_format_long) {
1849             parsed = expand_template(b->title_format_long, FALSE);
1850             gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1851             g_free(parsed);
1852         }
1853     }
1854 }
1855
1856 static gboolean
1857 key_press_cb (GtkWidget* window, GdkEventKey* event)
1858 {
1859     //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1860
1861     (void) window;
1862
1863     if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1864         || 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)
1865         return FALSE;
1866
1867     /* turn off insert mode (if always_insert_mode is not used) */
1868     if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1869         uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1870         update_title();
1871         return TRUE;
1872     }
1873
1874     if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1875         return FALSE;
1876
1877     if (event->keyval == GDK_Escape) {
1878         g_string_truncate(uzbl.state.keycmd, 0);
1879         update_title();
1880         dehilight(uzbl.gui.web_view, NULL);
1881         return TRUE;
1882     }
1883
1884     //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1885     if (event->keyval == GDK_Insert) {
1886         gchar * str;
1887         if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1888             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1889         } else {
1890             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1891         }
1892         if (str) {
1893             g_string_append (uzbl.state.keycmd, str);
1894             update_title ();
1895             g_free (str);
1896         }
1897         return TRUE;
1898     }
1899
1900     if (event->keyval == GDK_BackSpace)
1901         keycmd_bs(NULL, NULL);
1902
1903     gboolean key_ret = FALSE;
1904     if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1905         key_ret = TRUE;
1906     if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1907
1908     run_keycmd(key_ret);
1909     update_title();
1910     if (key_ret) return (!uzbl.behave.insert_mode);
1911     return TRUE;
1912 }
1913
1914 static void
1915 run_keycmd(const gboolean key_ret) {
1916     /* run the keycmd immediately if it isn't incremental and doesn't take args */
1917     Action *act;
1918     if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1919         g_string_truncate(uzbl.state.keycmd, 0);
1920         parse_command(act->name, act->param);
1921         return;
1922     }
1923
1924     /* try if it's an incremental keycmd or one that takes args, and run it */
1925     GString* short_keys = g_string_new ("");
1926     GString* short_keys_inc = g_string_new ("");
1927     guint i;
1928     for (i=0; i<(uzbl.state.keycmd->len); i++) {
1929         g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1930         g_string_assign(short_keys_inc, short_keys->str);
1931         g_string_append_c(short_keys, '_');
1932         g_string_append_c(short_keys_inc, '*');
1933
1934         if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1935             /* run normal cmds only if return was pressed */
1936             exec_paramcmd(act, i);
1937             g_string_truncate(uzbl.state.keycmd, 0);
1938             break;
1939         } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1940             if (key_ret)  /* just quit the incremental command on return */
1941                 g_string_truncate(uzbl.state.keycmd, 0);
1942             else exec_paramcmd(act, i); /* otherwise execute the incremental */
1943             break;
1944         }
1945         
1946         g_string_truncate(short_keys, short_keys->len - 1);
1947     }
1948     g_string_free (short_keys, TRUE);
1949     g_string_free (short_keys_inc, TRUE);
1950 }
1951
1952 static void
1953 exec_paramcmd(const Action *act, const guint i) {
1954     GString *parampart = g_string_new (uzbl.state.keycmd->str);
1955     GString *actionname = g_string_new ("");
1956     GString *actionparam = g_string_new ("");
1957     g_string_erase (parampart, 0, i+1);
1958     if (act->name)
1959         g_string_printf (actionname, act->name, parampart->str);
1960     if (act->param)
1961         g_string_printf (actionparam, act->param, parampart->str);
1962     parse_command(actionname->str, actionparam->str);
1963     g_string_free(actionname, TRUE);
1964     g_string_free(actionparam, TRUE);
1965     g_string_free(parampart, TRUE);
1966 }
1967
1968
1969 static GtkWidget*
1970 create_browser () {
1971     GUI *g = &uzbl.gui;
1972
1973     GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1974     //main_window_ref = g_object_ref(scrolled_window);
1975     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
1976
1977     g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1978     gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1979
1980     g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1981     g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1982     g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1983     g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
1984     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1985     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1986     g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1987     g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1988     g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1989     g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1990
1991     return scrolled_window;
1992 }
1993
1994 static GtkWidget*
1995 create_mainbar () {
1996     GUI *g = &uzbl.gui;
1997
1998     g->mainbar = gtk_hbox_new (FALSE, 0);
1999
2000     /* keep a reference to the bar so we can re-pack it at runtime*/
2001     //sbar_ref = g_object_ref(g->mainbar);
2002
2003     g->mainbar_label = gtk_label_new ("");
2004     gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2005     gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2006     gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2007     gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2008     gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2009     return g->mainbar;
2010 }
2011
2012 static
2013 GtkWidget* create_window () {
2014     GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2015     gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2016     gtk_widget_set_name (window, "Uzbl browser");
2017     g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2018     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2019
2020     return window;
2021 }
2022
2023 static gchar**
2024 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2025     /*
2026       If actname is one that calls an external command, this function will inject
2027       newargs in front of the user-provided args in that command line.  They will
2028       come become after the body of the script (in sh) or after the name of
2029       the command to execute (in spawn).
2030       i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2031       span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2032
2033       The return value consist of two strings: the action (sh, ...) and its args.
2034
2035       If act is not one that calls an external command, then the given action merely
2036       gets duplicated.
2037     */
2038     GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2039     gchar *actdup = g_strdup(actname);
2040     g_array_append_val(rets, actdup);
2041
2042     if ((g_strcmp0(actname, "spawn") == 0) ||
2043         (g_strcmp0(actname, "sh") == 0) ||
2044         (g_strcmp0(actname, "sync_spawn") == 0) ||
2045         (g_strcmp0(actname, "sync_sh") == 0)) {
2046         guint i;
2047         GString *a = g_string_new("");
2048         gchar **spawnparts = split_quoted(origargs, FALSE);
2049         g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2050         if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2051
2052         for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2053             if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2054
2055         g_array_append_val(rets, a->str);
2056         g_string_free(a, FALSE);
2057         g_strfreev(spawnparts);
2058     } else {
2059         gchar *origdup = g_strdup(origargs);
2060         g_array_append_val(rets, origdup);
2061     }
2062     return (gchar**)g_array_free(rets, FALSE);
2063 }
2064
2065 static void
2066 run_handler (const gchar *act, const gchar *args) {
2067     /* Consider this code a temporary hack to make the handlers usable.
2068        In practice, all this splicing, injection, and reconstruction is
2069        inefficient, annoying and hard to manage.  Potential pitfalls arise
2070        when the handler specific args 1) are not quoted  (the handler
2071        callbacks should take care of this)  2) are quoted but interfere
2072        with the users' own quotation.  A more ideal solution is
2073        to refactor parse_command so that it doesn't just take a string
2074        and execute it; rather than that, we should have a function which
2075        returns the argument vector parsed from the string.  This vector
2076        could be modified (e.g. insert additional args into it) before
2077        passing it to the next function that actually executes it.  Though
2078        it still isn't perfect for chain actions..  will reconsider & re-
2079        factor when I have the time. -duc */
2080
2081     char **parts = g_strsplit(act, " ", 2);
2082     if (!parts) return;
2083     if (g_strcmp0(parts[0], "chain") == 0) {
2084         GString *newargs = g_string_new("");
2085         gchar **chainparts = split_quoted(parts[1], FALSE);
2086         
2087         /* for every argument in the chain, inject the handler args
2088            and make sure the new parts are wrapped in quotes */
2089         gchar **cp = chainparts;
2090         gchar quot = '\'';
2091         gchar *quotless = NULL;
2092         gchar **spliced_quotless = NULL; // sigh -_-;
2093         gchar **inpart = NULL;
2094         
2095         while (*cp) {
2096             if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2097                 quot = **cp;
2098                 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2099             } else quotless = g_strdup(*cp);
2100
2101             spliced_quotless = g_strsplit(quotless, " ", 2);
2102             inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2103             g_strfreev(spliced_quotless);
2104             
2105             g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2106             g_free(quotless);
2107             g_strfreev(inpart);
2108             cp++;
2109         }
2110
2111         parse_command(parts[0], &(newargs->str[1]));
2112         g_string_free(newargs, TRUE);
2113         g_strfreev(chainparts);
2114         
2115     } else {
2116         gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2117         parse_command(inparts[0], inparts[1]);
2118         g_free(inparts[0]);
2119         g_free(inparts[1]);
2120     }
2121     g_strfreev(parts);
2122 }
2123
2124 static void
2125 add_binding (const gchar *key, const gchar *act) {
2126     char **parts = g_strsplit(act, " ", 2);
2127     Action *action;
2128
2129     if (!parts)
2130         return;
2131
2132     //Debug:
2133     if (uzbl.state.verbose)
2134         printf ("Binding %-10s : %s\n", key, act);
2135     action = new_action(parts[0], parts[1]);
2136
2137     g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2138     g_strfreev(parts);
2139 }
2140
2141 static gchar*
2142 get_xdg_var (XDG_Var xdg) {
2143     const gchar* actual_value = getenv (xdg.environmental);
2144     const gchar* home         = getenv ("HOME");
2145
2146     gchar* return_value = str_replace ("~", home, actual_value);
2147
2148     if (! actual_value || strcmp (actual_value, "") == 0) {
2149         if (xdg.default_value) {
2150             return_value = str_replace ("~", home, xdg.default_value);
2151         } else {
2152             return_value = NULL;
2153         }
2154     }
2155     return return_value;
2156 }
2157
2158 static gchar*
2159 find_xdg_file (int xdg_type, char* filename) {
2160     /* xdg_type = 0 => config
2161        xdg_type = 1 => data
2162        xdg_type = 2 => cache*/
2163
2164     gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2165     gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2166     g_free (xdgv);
2167
2168     gchar* temporary_string;
2169     char*  saveptr;
2170     char*  buf;
2171
2172     if (! file_exists (temporary_file) && xdg_type != 2) {
2173         buf = get_xdg_var (XDG[3 + xdg_type]);
2174         temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2175         g_free(buf);
2176
2177         while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2178             g_free (temporary_file);
2179             temporary_file = g_strconcat (temporary_string, filename, NULL);
2180         }
2181     }
2182     
2183     //g_free (temporary_string); - segfaults.
2184
2185     if (file_exists (temporary_file)) {
2186         return temporary_file;
2187     } else {
2188         return NULL;
2189     }
2190 }
2191 static void
2192 settings_init () {
2193     State *s = &uzbl.state;
2194     Network *n = &uzbl.net;
2195     int i;
2196     for (i = 0; default_config[i].command != NULL; i++) {
2197         parse_cmd_line(default_config[i].command);
2198     }
2199
2200     if (!s->config_file) {
2201         s->config_file = find_xdg_file (0, "/uzbl/config");
2202     }
2203
2204     if (s->config_file) {
2205         GArray* lines = read_file_by_line (s->config_file);
2206         int i = 0;
2207         gchar* line;
2208
2209         while ((line = g_array_index(lines, gchar*, i))) {
2210             parse_cmd_line (line);
2211             i ++;
2212             g_free (line);
2213         }
2214         g_array_free (lines, TRUE);
2215     } else {
2216         if (uzbl.state.verbose)
2217             printf ("No configuration file loaded.\n");
2218     }
2219
2220     g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
2221 }
2222
2223 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2224     (void) session;
2225     (void) user_data;
2226     if (!uzbl.behave.cookie_handler) return;
2227
2228     soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2229     GString *s = g_string_new ("");
2230     SoupURI * soup_uri = soup_message_get_uri(msg);
2231     g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2232     run_handler(uzbl.behave.cookie_handler, s->str);
2233
2234     if(uzbl.comm.sync_stdout)
2235         soup_message_headers_replace (msg->request_headers, "Cookie", uzbl.comm.sync_stdout);
2236     //printf("stdout: %s\n", uzbl.comm.sync_stdout);   // debugging
2237     if (uzbl.comm.sync_stdout) uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2238         
2239     g_string_free(s, TRUE);
2240 }
2241
2242 static void
2243 save_cookies (SoupMessage *msg, gpointer user_data){
2244     (void) user_data;
2245     GSList *ck;
2246     char *cookie;
2247     for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2248         cookie = soup_cookie_to_set_cookie_header(ck->data);
2249         SoupURI * soup_uri = soup_message_get_uri(msg);
2250         GString *s = g_string_new ("");
2251         g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2252         run_handler(uzbl.behave.cookie_handler, s->str);
2253         g_free (cookie);
2254         g_string_free(s, TRUE);
2255     }
2256     g_slist_free(ck);
2257 }
2258
2259 /* --- WEBINSPECTOR --- */
2260 static void
2261 hide_window_cb(GtkWidget *widget, gpointer data) {
2262     (void) data;
2263
2264     gtk_widget_hide(widget);
2265 }
2266
2267 static WebKitWebView*
2268 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2269     (void) data;
2270     (void) page;
2271     (void) web_inspector;
2272     GtkWidget* scrolled_window;
2273     GtkWidget* new_web_view;
2274     GUI *g = &uzbl.gui;
2275
2276     g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2277     g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2278             G_CALLBACK(hide_window_cb), NULL);
2279
2280     gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2281     gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2282     gtk_widget_show(g->inspector_window);
2283
2284     scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2285     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2286             GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2287     gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2288     gtk_widget_show(scrolled_window);
2289
2290     new_web_view = webkit_web_view_new();
2291     gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2292
2293     return WEBKIT_WEB_VIEW(new_web_view);
2294 }
2295
2296 static gboolean
2297 inspector_show_window_cb (WebKitWebInspector* inspector){
2298     (void) inspector;
2299     gtk_widget_show(uzbl.gui.inspector_window);
2300     return TRUE;
2301 }
2302
2303 /* TODO: Add variables and code to make use of these functions */
2304 static gboolean
2305 inspector_close_window_cb (WebKitWebInspector* inspector){
2306     (void) inspector;
2307     return TRUE;
2308 }
2309
2310 static gboolean
2311 inspector_attach_window_cb (WebKitWebInspector* inspector){
2312     (void) inspector;
2313     return FALSE;
2314 }
2315
2316 static gboolean
2317 inspector_dettach_window_cb (WebKitWebInspector* inspector){
2318     (void) inspector;
2319     return FALSE;
2320 }
2321
2322 static gboolean
2323 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2324     (void) inspector;
2325     return FALSE;
2326 }
2327
2328 static gboolean
2329 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2330     (void) inspector;
2331     return FALSE;
2332 }
2333
2334 static void
2335 set_up_inspector() {
2336     GUI *g = &uzbl.gui;
2337     WebKitWebSettings *settings = view_settings();
2338     g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2339
2340     uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2341     g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2342     g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2343     g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2344     g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2345     g_signal_connect (G_OBJECT (g->inspector), "dettach-window", G_CALLBACK (inspector_dettach_window_cb), NULL);
2346     g_signal_connect (G_OBJECT (g->inspector), "destroy", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2347
2348     g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2349 }
2350
2351 static void
2352 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2353     (void) ud;
2354     uzbl_cmdprop *c = v;
2355
2356     if(!c->dump)
2357         return;
2358
2359     if(c->type == TYPE_STR)
2360         printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2361     else if(c->type == TYPE_INT)
2362         printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2363 }
2364
2365 static void
2366 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2367     (void) ud;
2368     Action *a = v;
2369
2370     printf("bind %s = %s %s\n", (char *)k ,
2371             (char *)a->name, a->param?(char *)a->param:"");
2372 }
2373
2374 static void
2375 dump_config() {
2376     g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2377     g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2378 }
2379
2380 /** -- MAIN -- **/
2381 int
2382 main (int argc, char* argv[]) {
2383     gtk_init (&argc, &argv);
2384     if (!g_thread_supported ())
2385         g_thread_init (NULL);
2386     uzbl.state.executable_path = g_strdup(argv[0]);
2387     uzbl.state.selected_url = NULL;
2388     uzbl.state.searchtx = NULL;
2389
2390     GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2391     g_option_context_add_main_entries (context, entries, NULL);
2392     g_option_context_add_group (context, gtk_get_option_group (TRUE));
2393     g_option_context_parse (context, &argc, &argv, NULL);
2394     g_option_context_free(context);
2395     /* initialize hash table */
2396     uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2397
2398     uzbl.net.soup_session = webkit_get_default_session();
2399     uzbl.state.keycmd = g_string_new("");
2400
2401     if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2402         fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2403     if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2404         fprintf(stderr, "uzbl: error hooking SIGINT\n");
2405     if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2406         fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2407
2408
2409     if(uname(&uzbl.state.unameinfo) == -1)
2410         g_printerr("Can't retrieve unameinfo.  Your useragent might appear wrong.\n");
2411
2412     uzbl.gui.sbar.progress_s = g_strdup("=");
2413     uzbl.gui.sbar.progress_u = g_strdup("ยท");
2414     uzbl.gui.sbar.progress_w = 10;
2415
2416     /* HTML mode defaults*/
2417     uzbl.behave.html_buffer = g_string_new("");
2418     uzbl.behave.html_endmarker = g_strdup(".");
2419     uzbl.behave.html_timeout = 60;
2420     uzbl.behave.base_url = g_strdup("http://invalid");
2421
2422     /* default mode indicators */
2423     uzbl.behave.insert_indicator = g_strdup("I");
2424     uzbl.behave.cmd_indicator    = g_strdup("C");
2425
2426     setup_scanner();
2427     commands_hash ();
2428     make_var_to_name_hash();
2429
2430     uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2431
2432     uzbl.gui.scrolled_win = create_browser();
2433     create_mainbar();
2434
2435     /* initial packing */
2436     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2437     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2438
2439     uzbl.gui.main_window = create_window ();
2440     gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2441
2442
2443     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2444     gtk_widget_show_all (uzbl.gui.main_window);
2445     uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2446
2447     if (uzbl.state.verbose) {
2448         printf("Uzbl start location: %s\n", argv[0]);
2449         printf("window_id %i\n",(int) uzbl.xwin);
2450         printf("pid %i\n", getpid ());
2451         printf("name: %s\n", uzbl.state.instance_name);
2452     }
2453
2454     uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2455     uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2456     uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2457     uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2458     gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2459
2460     settings_init ();
2461
2462     if (!uzbl.behave.show_status)
2463         gtk_widget_hide(uzbl.gui.mainbar);
2464     else
2465         update_title();
2466
2467     /* WebInspector */
2468     set_up_inspector();
2469
2470     create_stdin();
2471
2472     if(uzbl.state.uri) {
2473         GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
2474         g_array_append_val(a, uzbl.state.uri);
2475         load_uri (uzbl.gui.web_view, a);
2476         g_array_free (a, TRUE);
2477     }
2478
2479     gtk_main ();
2480     clean_up();
2481
2482     return EXIT_SUCCESS;
2483 }
2484
2485 /* vi: set et ts=4: */