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