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