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