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