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