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