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