fixed buffer overrun
[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_indicator)},
123     { "command_indicator",   PTR_V(uzbl.behave.cmd_indicator,       STR,  1,   update_indicator)},
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[3];
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 update_indicator() {
918   set_mode_indicator();
919   update_title();
920 }
921
922 void
923 set_insert_mode(gboolean mode) {
924     uzbl.behave.insert_mode = mode;
925     set_mode_indicator();
926 }
927
928 void
929 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
930     (void) page; (void) result;
931
932     if (argv_idx(argv, 0)) {
933         if (strcmp (argv_idx(argv, 0), "0") == 0) {
934             set_insert_mode(FALSE);
935         } else {
936             set_insert_mode(TRUE);
937         }
938     } else {
939         set_insert_mode( !uzbl.behave.insert_mode );
940     }
941
942     update_title();
943 }
944
945 void
946 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
947     (void) result;
948
949     if (argv_idx(argv, 0)) {
950         GString* newuri = g_string_new (argv_idx(argv, 0));
951         if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
952             run_js(web_view, argv, NULL);
953             return;
954         }
955         if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
956             g_string_prepend (newuri, "http://");
957         /* if we do handle cookies, ask our handler for them */
958         webkit_web_view_load_uri (web_view, newuri->str);
959         g_string_free (newuri, TRUE);
960     }
961 }
962
963 /* Javascript*/
964
965 JSValueRef
966 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
967                 size_t argumentCount, const JSValueRef arguments[],
968                 JSValueRef* exception) {
969     (void) function;
970     (void) thisObject;
971     (void) exception;
972
973     JSStringRef js_result_string;
974     GString *result = g_string_new("");
975
976     if (argumentCount >= 1) {
977         JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
978         size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
979         char ctl_line[arg_size];
980         JSStringGetUTF8CString(arg, ctl_line, arg_size);
981
982         parse_cmd_line(ctl_line, result);
983
984         JSStringRelease(arg);
985     }
986     js_result_string = JSStringCreateWithUTF8CString(result->str);
987
988     g_string_free(result, TRUE);
989
990     return JSValueMakeString(ctx, js_result_string);
991 }
992
993 JSStaticFunction js_static_functions[] = {
994     {"run", js_run_command, kJSPropertyAttributeNone},
995 };
996
997 void
998 js_init() {
999     /* This function creates the class and its definition, only once */
1000     if (!uzbl.js.initialized) {
1001         /* it would be pretty cool to make this dynamic */
1002         uzbl.js.classdef = kJSClassDefinitionEmpty;
1003         uzbl.js.classdef.staticFunctions = js_static_functions;
1004
1005         uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
1006     }
1007 }
1008
1009
1010 void
1011 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
1012     WebKitWebFrame *frame;
1013     JSGlobalContextRef context;
1014     JSObjectRef globalobject;
1015     JSStringRef var_name;
1016
1017     JSStringRef js_script;
1018     JSValueRef js_result;
1019     JSStringRef js_result_string;
1020     size_t js_result_size;
1021
1022     js_init();
1023
1024     frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
1025     context = webkit_web_frame_get_global_context(frame);
1026     globalobject = JSContextGetGlobalObject(context);
1027
1028     /* uzbl javascript namespace */
1029     var_name = JSStringCreateWithUTF8CString("Uzbl");
1030     JSObjectSetProperty(context, globalobject, var_name,
1031                         JSObjectMake(context, uzbl.js.classref, NULL),
1032                         kJSClassAttributeNone, NULL);
1033
1034     /* evaluate the script and get return value*/
1035     js_script = JSStringCreateWithUTF8CString(script);
1036     js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1037     if (js_result && !JSValueIsUndefined(context, js_result)) {
1038         js_result_string = JSValueToStringCopy(context, js_result, NULL);
1039         js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1040
1041         if (js_result_size) {
1042             char js_result_utf8[js_result_size];
1043             JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1044             g_string_assign(result, js_result_utf8);
1045         }
1046
1047         JSStringRelease(js_result_string);
1048     }
1049
1050     /* cleanup */
1051     JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1052
1053     JSStringRelease(var_name);
1054     JSStringRelease(js_script);
1055 }
1056
1057 void
1058 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1059     if (argv_idx(argv, 0))
1060         eval_js(web_view, argv_idx(argv, 0), result);
1061 }
1062
1063 void
1064 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1065     (void) result;
1066     if (argv_idx(argv, 0)) {
1067         GArray* lines = read_file_by_line (argv_idx (argv, 0));
1068         gchar*  js = NULL;
1069         int i = 0;
1070         gchar* line;
1071
1072         while ((line = g_array_index(lines, gchar*, i))) {
1073             if (js == NULL) {
1074                 js = g_strdup (line);
1075             } else {
1076                 gchar* newjs = g_strconcat (js, line, NULL);
1077                 js = newjs;
1078             }
1079             i ++;
1080             g_free (line);
1081         }
1082
1083         if (uzbl.state.verbose)
1084             printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1085
1086         if (argv_idx (argv, 1)) {
1087             gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1088             g_free (js);
1089             js = newjs;
1090         }
1091         eval_js (web_view, js, result);
1092         g_free (js);
1093         g_array_free (lines, TRUE);
1094     }
1095 }
1096
1097 void
1098 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1099     if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1100         if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1101             webkit_web_view_unmark_text_matches (page);
1102             webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1103             uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1104         }
1105     }
1106
1107     if (uzbl.state.searchtx) {
1108         if (uzbl.state.verbose)
1109             printf ("Searching: %s\n", uzbl.state.searchtx);
1110         webkit_web_view_set_highlight_text_matches (page, TRUE);
1111         webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1112     }
1113 }
1114
1115 void
1116 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1117     (void) result;
1118     search_text(page, argv, TRUE);
1119 }
1120
1121 void
1122 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1123     (void) result;
1124     search_text(page, argv, FALSE);
1125 }
1126
1127 void
1128 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1129     (void) argv; (void) result;
1130     webkit_web_view_set_highlight_text_matches (page, FALSE);
1131 }
1132
1133
1134 void
1135 new_window_load_uri (const gchar * uri) {
1136     if (uzbl.behave.new_window) {
1137         GString *s = g_string_new ("");
1138         g_string_printf(s, "'%s'", uri);
1139         run_handler(uzbl.behave.new_window, s->str);
1140         return;
1141     }
1142     GString* to_execute = g_string_new ("");
1143     g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1144     int i;
1145     for (i = 0; entries[i].long_name != NULL; i++) {
1146         if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1147             gchar** str = (gchar**)entries[i].arg_data;
1148             if (*str!=NULL) {
1149                 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1150             }
1151         }
1152     }
1153     if (uzbl.state.verbose)
1154         printf("\n%s\n", to_execute->str);
1155     g_spawn_command_line_async (to_execute->str, NULL);
1156     g_string_free (to_execute, TRUE);
1157 }
1158
1159 void
1160 chain (WebKitWebView *page, GArray *argv, GString *result) {
1161     (void) page; (void) result;
1162     gchar *a = NULL;
1163     gchar **parts = NULL;
1164     guint i = 0;
1165     while ((a = argv_idx(argv, i++))) {
1166         parts = g_strsplit (a, " ", 2);
1167         if (parts[0])
1168           parse_command(parts[0], parts[1], result);
1169         g_strfreev (parts);
1170     }
1171 }
1172
1173 void
1174 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1175     (void)page;
1176     (void)argv;
1177     (void)result;
1178     uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1179     run_keycmd(FALSE);
1180     update_title();
1181 }
1182
1183 void
1184 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1185     (void)page;
1186     (void)argv;
1187     (void)result;
1188     uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1189     run_keycmd(TRUE);
1190     update_title();
1191 }
1192
1193 void
1194 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1195     gchar *prev;
1196     (void)page;
1197     (void)argv;
1198     (void)result;
1199     int len = strlen(uzbl.state.keycmd);
1200     prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1201     if (prev)
1202       uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1203     update_title();
1204 }
1205
1206 void
1207 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1208     (void)page;
1209     (void)argv;
1210     (void)result;
1211     gtk_main_quit ();
1212 }
1213
1214 /* --Statusbar functions-- */
1215 char*
1216 build_progressbar_ascii(int percent) {
1217    int width=uzbl.gui.sbar.progress_w;
1218    int i;
1219    double l;
1220    GString *bar = g_string_new("");
1221
1222    l = (double)percent*((double)width/100.);
1223    l = (int)(l+.5)>=(int)l ? l+.5 : l;
1224
1225    for(i=0; i<(int)l; i++)
1226        g_string_append(bar, uzbl.gui.sbar.progress_s);
1227
1228    for(; i<width; i++)
1229        g_string_append(bar, uzbl.gui.sbar.progress_u);
1230
1231    return g_string_free(bar, FALSE);
1232 }
1233 /* --End Statusbar functions-- */
1234
1235 void
1236 sharg_append(GArray *a, const gchar *str) {
1237     const gchar *s = (str ? str : "");
1238     g_array_append_val(a, s);
1239 }
1240
1241 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1242 gboolean
1243 run_command (const gchar *command, const guint npre, const gchar **args,
1244              const gboolean sync, char **output_stdout) {
1245    //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1246     GError *err = NULL;
1247
1248     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1249     gchar *pid = itos(getpid());
1250     gchar *xwin = itos(uzbl.xwin);
1251     guint i;
1252     sharg_append(a, command);
1253     for (i = 0; i < npre; i++) /* add n args before the default vars */
1254         sharg_append(a, args[i]);
1255     sharg_append(a, uzbl.state.config_file);
1256     sharg_append(a, pid);
1257     sharg_append(a, xwin);
1258     sharg_append(a, uzbl.comm.fifo_path);
1259     sharg_append(a, uzbl.comm.socket_path);
1260     sharg_append(a, uzbl.state.uri);
1261     sharg_append(a, uzbl.gui.main_title);
1262
1263     for (i = npre; i < g_strv_length((gchar**)args); i++)
1264         sharg_append(a, args[i]);
1265
1266     gboolean result;
1267     if (sync) {
1268         if (*output_stdout) *output_stdout = strfree(*output_stdout);
1269
1270         result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1271                               NULL, NULL, output_stdout, NULL, NULL, &err);
1272     } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1273                                   NULL, NULL, NULL, &err);
1274
1275     if (uzbl.state.verbose) {
1276         GString *s = g_string_new("spawned:");
1277         for (i = 0; i < (a->len); i++) {
1278             gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1279             g_string_append_printf(s, " %s", qarg);
1280             g_free (qarg);
1281         }
1282         g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1283         printf("%s\n", s->str);
1284         g_string_free(s, TRUE);
1285         if(output_stdout) {
1286             printf("Stdout: %s\n", *output_stdout);
1287         }
1288     }
1289     if (err) {
1290         g_printerr("error on run_command: %s\n", err->message);
1291         g_error_free (err);
1292     }
1293     g_free (pid);
1294     g_free (xwin);
1295     g_array_free (a, TRUE);
1296     return result;
1297 }
1298
1299 gchar**
1300 split_quoted(const gchar* src, const gboolean unquote) {
1301     /* split on unquoted space, return array of strings;
1302        remove a layer of quotes and backslashes if unquote */
1303     if (!src) return NULL;
1304
1305     gboolean dq = FALSE;
1306     gboolean sq = FALSE;
1307     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1308     GString *s = g_string_new ("");
1309     const gchar *p;
1310     gchar **ret;
1311     gchar *dup;
1312     for (p = src; *p != '\0'; p++) {
1313         if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1314         else if (*p == '\\') { g_string_append_c(s, *p++);
1315                                g_string_append_c(s, *p); }
1316         else if ((*p == '"') && unquote && !sq) dq = !dq;
1317         else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1318                                      dq = !dq; }
1319         else if ((*p == '\'') && unquote && !dq) sq = !sq;
1320         else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1321                                       sq = ! sq; }
1322         else if ((*p == ' ') && !dq && !sq) {
1323             dup = g_strdup(s->str);
1324             g_array_append_val(a, dup);
1325             g_string_truncate(s, 0);
1326         } else g_string_append_c(s, *p);
1327     }
1328     dup = g_strdup(s->str);
1329     g_array_append_val(a, dup);
1330     ret = (gchar**)a->data;
1331     g_array_free (a, FALSE);
1332     g_string_free (s, TRUE);
1333     return ret;
1334 }
1335
1336 void
1337 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1338     (void)web_view; (void)result;
1339     //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1340     if (argv_idx(argv, 0))
1341         run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1342 }
1343
1344 void
1345 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1346     (void)web_view; (void)result;
1347
1348     if (argv_idx(argv, 0))
1349         run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1350                     TRUE, &uzbl.comm.sync_stdout);
1351 }
1352
1353 void
1354 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1355     (void)web_view; (void)result;
1356     if (!uzbl.behave.shell_cmd) {
1357         g_printerr ("spawn_sh: shell_cmd is not set!\n");
1358         return;
1359     }
1360
1361     guint i;
1362     gchar *spacer = g_strdup("");
1363     g_array_insert_val(argv, 1, spacer);
1364     gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1365
1366     for (i = 1; i < g_strv_length(cmd); i++)
1367         g_array_prepend_val(argv, cmd[i]);
1368
1369     if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1370     g_free (spacer);
1371     g_strfreev (cmd);
1372 }
1373
1374 void
1375 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1376     (void)web_view; (void)result;
1377     if (!uzbl.behave.shell_cmd) {
1378         g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1379         return;
1380     }
1381
1382     guint i;
1383     gchar *spacer = g_strdup("");
1384     g_array_insert_val(argv, 1, spacer);
1385     gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1386
1387     for (i = 1; i < g_strv_length(cmd); i++)
1388         g_array_prepend_val(argv, cmd[i]);
1389
1390     if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1391                          TRUE, &uzbl.comm.sync_stdout);
1392     g_free (spacer);
1393     g_strfreev (cmd);
1394 }
1395
1396 void
1397 parse_command(const char *cmd, const char *param, GString *result) {
1398     CommandInfo *c;
1399
1400     if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1401             guint i;
1402             gchar **par = split_quoted(param, TRUE);
1403             GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1404
1405             if (c->no_split) { /* don't split */
1406                 sharg_append(a, param);
1407             } else if (par) {
1408                 for (i = 0; i < g_strv_length(par); i++)
1409                     sharg_append(a, par[i]);
1410             }
1411
1412             if (result == NULL) {
1413                 GString *result_print = g_string_new("");
1414
1415                 c->function(uzbl.gui.web_view, a, result_print);
1416                 if (result_print->len)
1417                     printf("%*s\n", result_print->len, result_print->str);
1418
1419                 g_string_free(result_print, TRUE);
1420             } else {
1421                 c->function(uzbl.gui.web_view, a, result);
1422             }
1423             g_strfreev (par);
1424             g_array_free (a, TRUE);
1425
1426     } else
1427         g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1428 }
1429
1430 void
1431 set_proxy_url() {
1432     SoupURI *suri;
1433
1434     if(*uzbl.net.proxy_url == ' '
1435        || uzbl.net.proxy_url == NULL) {
1436         soup_session_remove_feature_by_type(uzbl.net.soup_session,
1437                 (GType) SOUP_SESSION_PROXY_URI);
1438     }
1439     else {
1440         suri = soup_uri_new(uzbl.net.proxy_url);
1441         g_object_set(G_OBJECT(uzbl.net.soup_session),
1442                 SOUP_SESSION_PROXY_URI,
1443                 suri, NULL);
1444         soup_uri_free(suri);
1445     }
1446     return;
1447 }
1448
1449 void
1450 set_icon() {
1451     if(file_exists(uzbl.gui.icon)) {
1452         if (uzbl.gui.main_window)
1453             gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1454     } else {
1455         g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1456     }
1457 }
1458
1459 void
1460 cmd_load_uri() {
1461     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1462     g_array_append_val (a, uzbl.state.uri);
1463     load_uri(uzbl.gui.web_view, a, NULL);
1464     g_array_free (a, TRUE);
1465 }
1466
1467 void
1468 cmd_always_insert_mode() {
1469     set_insert_mode(uzbl.behave.always_insert_mode);
1470     update_title();
1471 }
1472
1473 void
1474 cmd_max_conns() {
1475     g_object_set(G_OBJECT(uzbl.net.soup_session),
1476             SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1477 }
1478
1479 void
1480 cmd_max_conns_host() {
1481     g_object_set(G_OBJECT(uzbl.net.soup_session),
1482             SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1483 }
1484
1485 void
1486 cmd_http_debug() {
1487     soup_session_remove_feature
1488         (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1489     /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1490     /*g_free(uzbl.net.soup_logger);*/
1491
1492     uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1493     soup_session_add_feature(uzbl.net.soup_session,
1494             SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1495 }
1496
1497 WebKitWebSettings*
1498 view_settings() {
1499     return webkit_web_view_get_settings(uzbl.gui.web_view);
1500 }
1501
1502 void
1503 cmd_font_size() {
1504     WebKitWebSettings *ws = view_settings();
1505     if (uzbl.behave.font_size > 0) {
1506         g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1507     }
1508
1509     if (uzbl.behave.monospace_size > 0) {
1510         g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1511                       uzbl.behave.monospace_size, NULL);
1512     } else {
1513         g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1514                       uzbl.behave.font_size, NULL);
1515     }
1516 }
1517
1518 void
1519 cmd_zoom_level() {
1520     webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1521 }
1522
1523 void
1524 cmd_disable_plugins() {
1525     g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1526             !uzbl.behave.disable_plugins, NULL);
1527 }
1528
1529 void
1530 cmd_disable_scripts() {
1531     g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1532             !uzbl.behave.disable_scripts, NULL);
1533 }
1534
1535 void
1536 cmd_minimum_font_size() {
1537     g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1538             uzbl.behave.minimum_font_size, NULL);
1539 }
1540 void
1541 cmd_autoload_img() {
1542     g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1543             uzbl.behave.autoload_img, NULL);
1544 }
1545
1546
1547 void
1548 cmd_autoshrink_img() {
1549     g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1550             uzbl.behave.autoshrink_img, NULL);
1551 }
1552
1553
1554 void
1555 cmd_enable_spellcheck() {
1556     g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1557             uzbl.behave.enable_spellcheck, NULL);
1558 }
1559
1560 void
1561 cmd_enable_private() {
1562     g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1563             uzbl.behave.enable_private, NULL);
1564 }
1565
1566 void
1567 cmd_print_bg() {
1568     g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1569             uzbl.behave.print_bg, NULL);
1570 }
1571
1572 void
1573 cmd_style_uri() {
1574     g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1575             uzbl.behave.style_uri, NULL);
1576 }
1577
1578 void
1579 cmd_resizable_txt() {
1580     g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1581             uzbl.behave.resizable_txt, NULL);
1582 }
1583
1584 void
1585 cmd_default_encoding() {
1586     g_object_set (G_OBJECT(view_settings()), "default-encoding",
1587             uzbl.behave.default_encoding, NULL);
1588 }
1589
1590 void
1591 cmd_enforce_96dpi() {
1592     g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1593             uzbl.behave.enforce_96dpi, NULL);
1594 }
1595
1596 void
1597 cmd_caret_browsing() {
1598     g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1599             uzbl.behave.caret_browsing, NULL);
1600 }
1601
1602 void
1603 cmd_cookie_handler() {
1604     gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1605     /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1606     if ((g_strcmp0(split[0], "sh") == 0) ||
1607         (g_strcmp0(split[0], "spawn") == 0)) {
1608         g_free (uzbl.behave.cookie_handler);
1609         uzbl.behave.cookie_handler =
1610             g_strdup_printf("sync_%s %s", split[0], split[1]);
1611     }
1612     g_strfreev (split);
1613 }
1614
1615 void
1616 cmd_new_window() {
1617     gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1618     /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1619     if ((g_strcmp0(split[0], "sh") == 0) ||
1620         (g_strcmp0(split[0], "spawn") == 0)) {
1621         g_free (uzbl.behave.new_window);
1622         uzbl.behave.new_window =
1623             g_strdup_printf("%s %s", split[0], split[1]);
1624     }
1625     g_strfreev (split);
1626 }
1627
1628 void
1629 cmd_fifo_dir() {
1630     uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1631 }
1632
1633 void
1634 cmd_socket_dir() {
1635     uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1636 }
1637
1638 void
1639 cmd_inject_html() {
1640     if(uzbl.behave.inject_html) {
1641         webkit_web_view_load_html_string (uzbl.gui.web_view,
1642                 uzbl.behave.inject_html, NULL);
1643     }
1644 }
1645
1646 void
1647 cmd_modkey() {
1648     int i;
1649     char *buf;
1650
1651     buf = g_utf8_strup(uzbl.behave.modkey, -1);
1652     uzbl.behave.modmask = 0;
1653
1654     if(uzbl.behave.modkey)
1655         g_free(uzbl.behave.modkey);
1656     uzbl.behave.modkey = buf;
1657
1658     for (i = 0; modkeys[i].key != NULL; i++) {
1659         if (g_strrstr(buf, modkeys[i].key))
1660             uzbl.behave.modmask |= modkeys[i].mask;
1661     }
1662 }
1663
1664 void
1665 cmd_useragent() {
1666     if (*uzbl.net.useragent == ' ') {
1667         g_free (uzbl.net.useragent);
1668         uzbl.net.useragent = NULL;
1669     } else {
1670         g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1671             uzbl.net.useragent, NULL);
1672     }
1673 }
1674
1675 void
1676 move_statusbar() {
1677     gtk_widget_ref(uzbl.gui.scrolled_win);
1678     gtk_widget_ref(uzbl.gui.mainbar);
1679     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1680     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1681
1682     if(uzbl.behave.status_top) {
1683         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1684         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1685     }
1686     else {
1687         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1688         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1689     }
1690     gtk_widget_unref(uzbl.gui.scrolled_win);
1691     gtk_widget_unref(uzbl.gui.mainbar);
1692     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1693     return;
1694 }
1695
1696 gboolean
1697 set_var_value(gchar *name, gchar *val) {
1698     uzbl_cmdprop *c = NULL;
1699     char *endp = NULL;
1700     char *buf = NULL;
1701
1702     if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1703         if(!c->writeable) return FALSE;
1704
1705         /* check for the variable type */
1706         if (c->type == TYPE_STR) {
1707             buf = expand(val, 0);
1708             g_free(*c->ptr);
1709             *c->ptr = buf;
1710         } else if(c->type == TYPE_INT) {
1711             int *ip = (int *)c->ptr;
1712             buf = expand(val, 0);
1713             *ip = (int)strtoul(buf, &endp, 10);
1714             g_free(buf);
1715         } else if (c->type == TYPE_FLOAT) {
1716             float *fp = (float *)c->ptr;
1717             buf = expand(val, 0);
1718             *fp = strtod(buf, &endp);
1719             g_free(buf);
1720         }
1721
1722         /* invoke a command specific function */
1723         if(c->func) c->func();
1724     }
1725     return TRUE;
1726 }
1727
1728 void
1729 render_html() {
1730     Behaviour *b = &uzbl.behave;
1731
1732     if(b->html_buffer->str) {
1733         webkit_web_view_load_html_string (uzbl.gui.web_view,
1734                 b->html_buffer->str, b->base_url);
1735         g_string_free(b->html_buffer, TRUE);
1736         b->html_buffer = g_string_new("");
1737     }
1738 }
1739
1740 enum {M_CMD, M_HTML};
1741 void
1742 parse_cmd_line(const char *ctl_line, GString *result) {
1743     Behaviour *b = &uzbl.behave;
1744     size_t len=0;
1745
1746     if(b->mode == M_HTML) {
1747         len = strlen(b->html_endmarker);
1748         /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1749         if(len == strlen(ctl_line)-1 &&
1750            !strncmp(b->html_endmarker, ctl_line, len)) {
1751             set_timeout(0);
1752             set_var_value("mode", "0");
1753             render_html();
1754             return;
1755         }
1756         else {
1757             set_timeout(b->html_timeout);
1758             g_string_append(b->html_buffer, ctl_line);
1759         }
1760     }
1761     else if((ctl_line[0] == '#') /* Comments */
1762             || (ctl_line[0] == ' ')
1763             || (ctl_line[0] == '\n'))
1764         ; /* ignore these lines */
1765     else { /* parse a command */
1766         gchar *ctlstrip;
1767         gchar **tokens = NULL;
1768         len = strlen(ctl_line);
1769
1770         if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1771             ctlstrip = g_strndup(ctl_line, len - 1);
1772         else ctlstrip = g_strdup(ctl_line);
1773
1774         tokens = g_strsplit(ctlstrip, " ", 2);
1775         parse_command(tokens[0], tokens[1], result);
1776         g_free(ctlstrip);
1777         g_strfreev(tokens);
1778     }
1779 }
1780
1781 gchar*
1782 build_stream_name(int type, const gchar* dir) {
1783     State *s = &uzbl.state;
1784     gchar *str = NULL;
1785
1786     if (type == FIFO) {
1787         str = g_strdup_printf
1788             ("%s/uzbl_fifo_%s", dir, s->instance_name);
1789     } else if (type == SOCKET) {
1790         str = g_strdup_printf
1791             ("%s/uzbl_socket_%s", dir, s->instance_name);
1792     }
1793     return str;
1794 }
1795
1796 gboolean
1797 control_fifo(GIOChannel *gio, GIOCondition condition) {
1798     if (uzbl.state.verbose)
1799         printf("triggered\n");
1800     gchar *ctl_line;
1801     GIOStatus ret;
1802     GError *err = NULL;
1803
1804     if (condition & G_IO_HUP)
1805         g_error ("Fifo: Read end of pipe died!\n");
1806
1807     if(!gio)
1808        g_error ("Fifo: GIOChannel broke\n");
1809
1810     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1811     if (ret == G_IO_STATUS_ERROR) {
1812         g_error ("Fifo: Error reading: %s\n", err->message);
1813         g_error_free (err);
1814     }
1815
1816     parse_cmd_line(ctl_line, NULL);
1817     g_free(ctl_line);
1818
1819     return TRUE;
1820 }
1821
1822 gchar*
1823 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1824     if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1825         if (unlink(uzbl.comm.fifo_path) == -1)
1826             g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1827         g_free(uzbl.comm.fifo_path);
1828         uzbl.comm.fifo_path = NULL;
1829     }
1830
1831     GIOChannel *chan = NULL;
1832     GError *error = NULL;
1833     gchar *path = build_stream_name(FIFO, dir);
1834
1835     if (!file_exists(path)) {
1836         if (mkfifo (path, 0666) == 0) {
1837             // 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.
1838             chan = g_io_channel_new_file(path, "r+", &error);
1839             if (chan) {
1840                 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1841                     if (uzbl.state.verbose)
1842                         printf ("init_fifo: created successfully as %s\n", path);
1843                     uzbl.comm.fifo_path = path;
1844                     return dir;
1845                 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1846             } else g_warning ("init_fifo: can't open: %s\n", error->message);
1847         } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1848     } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1849
1850     /* if we got this far, there was an error; cleanup */
1851     if (error) g_error_free (error);
1852     g_free(dir);
1853     g_free(path);
1854     return NULL;
1855 }
1856
1857 gboolean
1858 control_stdin(GIOChannel *gio, GIOCondition condition) {
1859     (void) condition;
1860     gchar *ctl_line = NULL;
1861     GIOStatus ret;
1862
1863     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1864     if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1865         return FALSE;
1866
1867     parse_cmd_line(ctl_line, NULL);
1868     g_free(ctl_line);
1869
1870     return TRUE;
1871 }
1872
1873 void
1874 create_stdin () {
1875     GIOChannel *chan = NULL;
1876     GError *error = NULL;
1877
1878     chan = g_io_channel_unix_new(fileno(stdin));
1879     if (chan) {
1880         if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1881             g_error ("Stdin: could not add watch\n");
1882         } else {
1883             if (uzbl.state.verbose)
1884                 printf ("Stdin: watch added successfully\n");
1885         }
1886     } else {
1887         g_error ("Stdin: Error while opening: %s\n", error->message);
1888     }
1889     if (error) g_error_free (error);
1890 }
1891
1892 gboolean
1893 control_socket(GIOChannel *chan) {
1894     struct sockaddr_un remote;
1895     unsigned int t = sizeof(remote);
1896     int clientsock;
1897     GIOChannel *clientchan;
1898
1899     clientsock = accept (g_io_channel_unix_get_fd(chan),
1900                          (struct sockaddr *) &remote, &t);
1901
1902     if ((clientchan = g_io_channel_unix_new(clientsock))) {
1903         g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1904                        (GIOFunc) control_client_socket, clientchan);
1905     }
1906
1907     return TRUE;
1908 }
1909
1910 gboolean
1911 control_client_socket(GIOChannel *clientchan) {
1912     char *ctl_line;
1913     GString *result = g_string_new("");
1914     GError *error = NULL;
1915     GIOStatus ret;
1916     gsize len;
1917
1918     ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1919     if (ret == G_IO_STATUS_ERROR) {
1920         g_warning ("Error reading: %s\n", error->message);
1921         g_io_channel_shutdown(clientchan, TRUE, &error);
1922         return FALSE;
1923     } else if (ret == G_IO_STATUS_EOF) {
1924         /* shutdown and remove channel watch from main loop */
1925         g_io_channel_shutdown(clientchan, TRUE, &error);
1926         return FALSE;
1927     }
1928
1929     if (ctl_line) {
1930         parse_cmd_line (ctl_line, result);
1931         g_string_append_c(result, '\n');
1932         ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1933                                         &len, &error);
1934         if (ret == G_IO_STATUS_ERROR) {
1935             g_warning ("Error writing: %s", error->message);
1936         }
1937         g_io_channel_flush(clientchan, &error);
1938     }
1939
1940     if (error) g_error_free (error);
1941     g_string_free(result, TRUE);
1942     g_free(ctl_line);
1943     return TRUE;
1944 }
1945
1946 gchar*
1947 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1948     if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1949         if (unlink(uzbl.comm.socket_path) == -1)
1950             g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1951         g_free(uzbl.comm.socket_path);
1952         uzbl.comm.socket_path = NULL;
1953     }
1954
1955     if (*dir == ' ') {
1956         g_free(dir);
1957         return NULL;
1958     }
1959
1960     GIOChannel *chan = NULL;
1961     int sock, len;
1962     struct sockaddr_un local;
1963     gchar *path = build_stream_name(SOCKET, dir);
1964
1965     sock = socket (AF_UNIX, SOCK_STREAM, 0);
1966
1967     local.sun_family = AF_UNIX;
1968     strcpy (local.sun_path, path);
1969     unlink (local.sun_path);
1970
1971     len = strlen (local.sun_path) + sizeof (local.sun_family);
1972     if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1973         if (uzbl.state.verbose)
1974             printf ("init_socket: opened in %s\n", path);
1975         listen (sock, 5);
1976
1977         if( (chan = g_io_channel_unix_new(sock)) ) {
1978             g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1979             uzbl.comm.socket_path = path;
1980             return dir;
1981         }
1982     } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1983
1984     /* if we got this far, there was an error; cleanup */
1985     g_free(path);
1986     g_free(dir);
1987     return NULL;
1988 }
1989
1990 /*
1991  NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1992  it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1993 */
1994 // this function may be called very early when the templates are not set (yet), hence the checks
1995 void
1996 update_title (void) {
1997     Behaviour *b = &uzbl.behave;
1998     gchar *parsed;
1999
2000     if (b->show_status) {
2001         if (b->title_format_short) {
2002             parsed = expand(b->title_format_short, 0);
2003             if (uzbl.gui.main_window)
2004                 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2005             g_free(parsed);
2006         }
2007         if (b->status_format) {
2008             parsed = expand(b->status_format, 0);
2009             gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2010             g_free(parsed);
2011         }
2012         if (b->status_background) {
2013             GdkColor color;
2014             gdk_color_parse (b->status_background, &color);
2015             //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)
2016             if (uzbl.gui.main_window)
2017                 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2018             else if (uzbl.gui.plug)
2019                 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2020         }
2021     } else {
2022         if (b->title_format_long) {
2023             parsed = expand(b->title_format_long, 0);
2024             if (uzbl.gui.main_window)
2025                 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2026             g_free(parsed);
2027         }
2028     }
2029 }
2030
2031 gboolean
2032 configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
2033     (void) window;
2034     (void) event;
2035
2036     retreive_geometry();
2037     return FALSE;
2038 }
2039
2040 gboolean
2041 key_press_cb (GtkWidget* window, GdkEventKey* event)
2042 {
2043     //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2044
2045     (void) window;
2046
2047     if (event->type   != GDK_KEY_PRESS ||
2048         event->keyval == GDK_Page_Up   ||
2049         event->keyval == GDK_Page_Down ||
2050         event->keyval == GDK_Up        ||
2051         event->keyval == GDK_Down      ||
2052         event->keyval == GDK_Left      ||
2053         event->keyval == GDK_Right     ||
2054         event->keyval == GDK_Shift_L   ||
2055         event->keyval == GDK_Shift_R)
2056         return FALSE;
2057
2058     /* turn off insert mode (if always_insert_mode is not used) */
2059     if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2060         set_insert_mode(uzbl.behave.always_insert_mode);
2061         update_title();
2062         return TRUE;
2063     }
2064
2065     if (uzbl.behave.insert_mode &&
2066         ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
2067           (!uzbl.behave.modmask)
2068         )
2069        )
2070         return FALSE;
2071
2072     if (event->keyval == GDK_Escape) {
2073         clear_keycmd();
2074         update_title();
2075         dehilight(uzbl.gui.web_view, NULL, NULL);
2076         return TRUE;
2077     }
2078
2079     //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2080     if (event->keyval == GDK_Insert) {
2081         gchar * str;
2082         if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2083             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2084         } else {
2085             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2086         }
2087         if (str) {
2088             GString* keycmd = g_string_new(uzbl.state.keycmd);
2089             g_string_append (keycmd, str);
2090             uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2091             update_title ();
2092             g_free (str);
2093         }
2094         return TRUE;
2095     }
2096
2097     if (event->keyval == GDK_BackSpace)
2098         keycmd_bs(NULL, NULL, NULL);
2099
2100     gboolean key_ret = FALSE;
2101     if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2102         key_ret = TRUE;
2103     if (!key_ret) {
2104         GString* keycmd = g_string_new(uzbl.state.keycmd);
2105         g_string_append(keycmd, event->string);
2106         uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2107     }
2108
2109     run_keycmd(key_ret);
2110     update_title();
2111     if (key_ret) return (!uzbl.behave.insert_mode);
2112     return TRUE;
2113 }
2114
2115 void
2116 run_keycmd(const gboolean key_ret) {
2117     /* run the keycmd immediately if it isn't incremental and doesn't take args */
2118     Action *act;
2119     if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2120         clear_keycmd();
2121         parse_command(act->name, act->param, NULL);
2122         return;
2123     }
2124
2125     /* try if it's an incremental keycmd or one that takes args, and run it */
2126     GString* short_keys = g_string_new ("");
2127     GString* short_keys_inc = g_string_new ("");
2128     guint i;
2129     guint len = strlen(uzbl.state.keycmd);
2130     for (i=0; i<len; i++) {
2131         g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2132         g_string_assign(short_keys_inc, short_keys->str);
2133         g_string_append_c(short_keys, '_');
2134         g_string_append_c(short_keys_inc, '*');
2135
2136         if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2137             /* run normal cmds only if return was pressed */
2138             exec_paramcmd(act, i);
2139             clear_keycmd();
2140             break;
2141         } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2142             if (key_ret)  /* just quit the incremental command on return */
2143                 clear_keycmd();
2144             else exec_paramcmd(act, i); /* otherwise execute the incremental */
2145             break;
2146         }
2147
2148         g_string_truncate(short_keys, short_keys->len - 1);
2149     }
2150     g_string_free (short_keys, TRUE);
2151     g_string_free (short_keys_inc, TRUE);
2152 }
2153
2154 void
2155 exec_paramcmd(const Action *act, const guint i) {
2156     GString *parampart = g_string_new (uzbl.state.keycmd);
2157     GString *actionname = g_string_new ("");
2158     GString *actionparam = g_string_new ("");
2159     g_string_erase (parampart, 0, i+1);
2160     if (act->name)
2161         g_string_printf (actionname, act->name, parampart->str);
2162     if (act->param)
2163         g_string_printf (actionparam, act->param, parampart->str);
2164     parse_command(actionname->str, actionparam->str, NULL);
2165     g_string_free(actionname, TRUE);
2166     g_string_free(actionparam, TRUE);
2167     g_string_free(parampart, TRUE);
2168 }
2169
2170
2171 GtkWidget*
2172 create_browser () {
2173     GUI *g = &uzbl.gui;
2174
2175     GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2176     //main_window_ref = g_object_ref(scrolled_window);
2177     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
2178
2179     g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2180     gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2181
2182     g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2183     g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2184     g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2185     g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2186     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2187     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2188     g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2189     g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2190     g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2191     g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2192     g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2193
2194     return scrolled_window;
2195 }
2196
2197 GtkWidget*
2198 create_mainbar () {
2199     GUI *g = &uzbl.gui;
2200
2201     g->mainbar = gtk_hbox_new (FALSE, 0);
2202
2203     /* keep a reference to the bar so we can re-pack it at runtime*/
2204     //sbar_ref = g_object_ref(g->mainbar);
2205
2206     g->mainbar_label = gtk_label_new ("");
2207     gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2208     gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2209     gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2210     gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2211     gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2212     g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2213     return g->mainbar;
2214 }
2215
2216 GtkWidget*
2217 create_window () {
2218     GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2219     gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2220     gtk_widget_set_name (window, "Uzbl browser");
2221     g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2222     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2223     g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2224
2225     return window;
2226 }
2227
2228 GtkPlug*
2229 create_plug () {
2230     GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2231     g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2232     g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2233
2234     return plug;
2235 }
2236
2237
2238 gchar**
2239 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2240     /*
2241       If actname is one that calls an external command, this function will inject
2242       newargs in front of the user-provided args in that command line.  They will
2243       come become after the body of the script (in sh) or after the name of
2244       the command to execute (in spawn).
2245       i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2246       spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2247
2248       The return value consist of two strings: the action (sh, ...) and its args.
2249
2250       If act is not one that calls an external command, then the given action merely
2251       gets duplicated.
2252     */
2253     GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2254     /* Arrr! Here be memory leaks */
2255     gchar *actdup = g_strdup(actname);
2256     g_array_append_val(rets, actdup);
2257
2258     if ((g_strcmp0(actname, "spawn") == 0) ||
2259         (g_strcmp0(actname, "sh") == 0) ||
2260         (g_strcmp0(actname, "sync_spawn") == 0) ||
2261         (g_strcmp0(actname, "sync_sh") == 0)) {
2262         guint i;
2263         GString *a = g_string_new("");
2264         gchar **spawnparts = split_quoted(origargs, FALSE);
2265         g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2266         if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2267
2268         for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2269             if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2270
2271         g_array_append_val(rets, a->str);
2272         g_string_free(a, FALSE);
2273         g_strfreev(spawnparts);
2274     } else {
2275         gchar *origdup = g_strdup(origargs);
2276         g_array_append_val(rets, origdup);
2277     }
2278     return (gchar**)g_array_free(rets, FALSE);
2279 }
2280
2281 void
2282 run_handler (const gchar *act, const gchar *args) {
2283     /* Consider this code a temporary hack to make the handlers usable.
2284        In practice, all this splicing, injection, and reconstruction is
2285        inefficient, annoying and hard to manage.  Potential pitfalls arise
2286        when the handler specific args 1) are not quoted  (the handler
2287        callbacks should take care of this)  2) are quoted but interfere
2288        with the users' own quotation.  A more ideal solution is
2289        to refactor parse_command so that it doesn't just take a string
2290        and execute it; rather than that, we should have a function which
2291        returns the argument vector parsed from the string.  This vector
2292        could be modified (e.g. insert additional args into it) before
2293        passing it to the next function that actually executes it.  Though
2294        it still isn't perfect for chain actions..  will reconsider & re-
2295        factor when I have the time. -duc */
2296
2297     char **parts = g_strsplit(act, " ", 2);
2298     if (!parts) return;
2299     if (g_strcmp0(parts[0], "chain") == 0) {
2300         GString *newargs = g_string_new("");
2301         gchar **chainparts = split_quoted(parts[1], FALSE);
2302
2303         /* for every argument in the chain, inject the handler args
2304            and make sure the new parts are wrapped in quotes */
2305         gchar **cp = chainparts;
2306         gchar quot = '\'';
2307         gchar *quotless = NULL;
2308         gchar **spliced_quotless = NULL; // sigh -_-;
2309         gchar **inpart = NULL;
2310
2311         while (*cp) {
2312             if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2313                 quot = **cp;
2314                 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2315             } else quotless = g_strdup(*cp);
2316
2317             spliced_quotless = g_strsplit(quotless, " ", 2);
2318             inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2319             g_strfreev(spliced_quotless);
2320
2321             g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2322             g_free(quotless);
2323             g_strfreev(inpart);
2324             cp++;
2325         }
2326
2327         parse_command(parts[0], &(newargs->str[1]), NULL);
2328         g_string_free(newargs, TRUE);
2329         g_strfreev(chainparts);
2330
2331     } else {
2332         gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2333         parse_command(inparts[0], inparts[1], NULL);
2334         g_free(inparts[0]);
2335         g_free(inparts[1]);
2336     }
2337     g_strfreev(parts);
2338 }
2339
2340 void
2341 add_binding (const gchar *key, const gchar *act) {
2342     char **parts = g_strsplit(act, " ", 2);
2343     Action *action;
2344
2345     if (!parts)
2346         return;
2347
2348     //Debug:
2349     if (uzbl.state.verbose)
2350         printf ("Binding %-10s : %s\n", key, act);
2351     action = new_action(parts[0], parts[1]);
2352
2353     if (g_hash_table_remove (uzbl.bindings, key))
2354         g_warning ("Overwriting existing binding for \"%s\"", key);
2355     g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2356     g_strfreev(parts);
2357 }
2358
2359 gchar*
2360 get_xdg_var (XDG_Var xdg) {
2361     const gchar* actual_value = getenv (xdg.environmental);
2362     const gchar* home         = getenv ("HOME");
2363     gchar* return_value;
2364
2365     if (! actual_value || strcmp (actual_value, "") == 0) {
2366         if (xdg.default_value) {
2367             return_value = str_replace ("~", home, xdg.default_value);
2368         } else {
2369             return_value = NULL;
2370         }
2371     } else {
2372         return_value = str_replace("~", home, actual_value);
2373     }
2374
2375     return return_value;
2376 }
2377
2378 gchar*
2379 find_xdg_file (int xdg_type, char* filename) {
2380     /* xdg_type = 0 => config
2381        xdg_type = 1 => data
2382        xdg_type = 2 => cache*/
2383
2384     gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2385     gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2386     g_free (xdgv);
2387
2388     gchar* temporary_string;
2389     char*  saveptr;
2390     char*  buf;
2391
2392     if (! file_exists (temporary_file) && xdg_type != 2) {
2393         buf = get_xdg_var (XDG[3 + xdg_type]);
2394         temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2395         g_free(buf);
2396
2397         while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2398             g_free (temporary_file);
2399             temporary_file = g_strconcat (temporary_string, filename, NULL);
2400         }
2401     }
2402
2403     //g_free (temporary_string); - segfaults.
2404
2405     if (file_exists (temporary_file)) {
2406         return temporary_file;
2407     } else {
2408         return NULL;
2409     }
2410 }
2411 void
2412 settings_init () {
2413     State *s = &uzbl.state;
2414     Network *n = &uzbl.net;
2415     int i;
2416     for (i = 0; default_config[i].command != NULL; i++) {
2417         parse_cmd_line(default_config[i].command, NULL);
2418     }
2419
2420     if (g_strcmp0(s->config_file, "-") == 0) {
2421         s->config_file = NULL;
2422         create_stdin();
2423     }
2424
2425     else if (!s->config_file) {
2426         s->config_file = find_xdg_file (0, "/uzbl/config");
2427     }
2428
2429     if (s->config_file) {
2430         GArray* lines = read_file_by_line (s->config_file);
2431         int i = 0;
2432         gchar* line;
2433
2434         while ((line = g_array_index(lines, gchar*, i))) {
2435             parse_cmd_line (line, NULL);
2436             i ++;
2437             g_free (line);
2438         }
2439         g_array_free (lines, TRUE);
2440     } else {
2441         if (uzbl.state.verbose)
2442             printf ("No configuration file loaded.\n");
2443     }
2444
2445     g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2446 }
2447
2448 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2449     (void) session;
2450     (void) user_data;
2451     if (!uzbl.behave.cookie_handler)
2452          return;
2453
2454     soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2455     GString *s = g_string_new ("");
2456     SoupURI * soup_uri = soup_message_get_uri(msg);
2457     g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2458     run_handler(uzbl.behave.cookie_handler, s->str);
2459
2460     if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2461         char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2462         if ( p != NULL ) *p = '\0';
2463         soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2464     }
2465     if (uzbl.comm.sync_stdout)
2466         uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2467
2468     g_string_free(s, TRUE);
2469 }
2470
2471 void
2472 save_cookies (SoupMessage *msg, gpointer user_data){
2473     (void) user_data;
2474     GSList *ck;
2475     char *cookie;
2476     for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2477         cookie = soup_cookie_to_set_cookie_header(ck->data);
2478         SoupURI * soup_uri = soup_message_get_uri(msg);
2479         GString *s = g_string_new ("");
2480         g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2481         run_handler(uzbl.behave.cookie_handler, s->str);
2482         g_free (cookie);
2483         g_string_free(s, TRUE);
2484     }
2485     g_slist_free(ck);
2486 }
2487
2488 /* --- WEBINSPECTOR --- */
2489 void
2490 hide_window_cb(GtkWidget *widget, gpointer data) {
2491     (void) data;
2492
2493     gtk_widget_hide(widget);
2494 }
2495
2496 WebKitWebView*
2497 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2498     (void) data;
2499     (void) page;
2500     (void) web_inspector;
2501     GtkWidget* scrolled_window;
2502     GtkWidget* new_web_view;
2503     GUI *g = &uzbl.gui;
2504
2505     g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2506     g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2507             G_CALLBACK(hide_window_cb), NULL);
2508
2509     gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2510     gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2511     gtk_widget_show(g->inspector_window);
2512
2513     scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2514     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2515             GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2516     gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2517     gtk_widget_show(scrolled_window);
2518
2519     new_web_view = webkit_web_view_new();
2520     gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2521
2522     return WEBKIT_WEB_VIEW(new_web_view);
2523 }
2524
2525 gboolean
2526 inspector_show_window_cb (WebKitWebInspector* inspector){
2527     (void) inspector;
2528     gtk_widget_show(uzbl.gui.inspector_window);
2529     return TRUE;
2530 }
2531
2532 /* TODO: Add variables and code to make use of these functions */
2533 gboolean
2534 inspector_close_window_cb (WebKitWebInspector* inspector){
2535     (void) inspector;
2536     return TRUE;
2537 }
2538
2539 gboolean
2540 inspector_attach_window_cb (WebKitWebInspector* inspector){
2541     (void) inspector;
2542     return FALSE;
2543 }
2544
2545 gboolean
2546 inspector_detach_window_cb (WebKitWebInspector* inspector){
2547     (void) inspector;
2548     return FALSE;
2549 }
2550
2551 gboolean
2552 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2553     (void) inspector;
2554     return FALSE;
2555 }
2556
2557 gboolean
2558 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2559     (void) inspector;
2560     return FALSE;
2561 }
2562
2563 void
2564 set_up_inspector() {
2565     GUI *g = &uzbl.gui;
2566     WebKitWebSettings *settings = view_settings();
2567     g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2568
2569     uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2570     g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2571     g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2572     g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2573     g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2574     g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2575     g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2576
2577     g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2578 }
2579
2580 void
2581 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2582     (void) ud;
2583     uzbl_cmdprop *c = v;
2584
2585     if(!c->dump)
2586         return;
2587
2588     if(c->type == TYPE_STR)
2589         printf("set %s = %s\n", (char *)k, *c->ptr ? (char *)*c->ptr : " ");
2590     else if(c->type == TYPE_INT)
2591         printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2592     else if(c->type == TYPE_FLOAT)
2593         printf("set %s = %f\n", (char *)k, *(float *)c->ptr);
2594 }
2595
2596 void
2597 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2598     (void) ud;
2599     Action *a = v;
2600
2601     printf("bind %s = %s %s\n", (char *)k ,
2602             (char *)a->name, a->param?(char *)a->param:"");
2603 }
2604
2605 void
2606 dump_config() {
2607     g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2608     g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2609 }
2610
2611 void
2612 retreive_geometry() {
2613     int w, h, x, y;
2614     GString *buf = g_string_new("");
2615
2616     gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2617     gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2618
2619     g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2620
2621     if(uzbl.gui.geometry)
2622         g_free(uzbl.gui.geometry);
2623     uzbl.gui.geometry = g_string_free(buf, FALSE);
2624 }
2625
2626 /* set up gtk, gobject, variable defaults and other things that tests and other
2627  * external applications need to do anyhow */
2628 void
2629 initialize(int argc, char *argv[]) {
2630     gtk_init (&argc, &argv);
2631     if (!g_thread_supported ())
2632         g_thread_init (NULL);
2633     uzbl.state.executable_path = g_strdup(argv[0]);
2634     uzbl.state.selected_url = NULL;
2635     uzbl.state.searchtx = NULL;
2636
2637     GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2638     g_option_context_add_main_entries (context, entries, NULL);
2639     g_option_context_add_group (context, gtk_get_option_group (TRUE));
2640     g_option_context_parse (context, &argc, &argv, NULL);
2641     g_option_context_free(context);
2642
2643     if (uzbl.behave.print_version) {
2644         printf("Commit: %s\n", COMMIT);
2645         exit(0);
2646     }
2647
2648     /* initialize hash table */
2649     uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2650
2651     uzbl.net.soup_session = webkit_get_default_session();
2652     uzbl.state.keycmd = g_strdup("");
2653
2654     if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2655         fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2656     if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2657         fprintf(stderr, "uzbl: error hooking SIGINT\n");
2658     if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2659         fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2660
2661     uzbl.gui.sbar.progress_s = g_strdup("="); //TODO: move these to config.h
2662     uzbl.gui.sbar.progress_u = g_strdup("ยท");
2663     uzbl.gui.sbar.progress_w = 10;
2664
2665     /* HTML mode defaults*/
2666     uzbl.behave.html_buffer = g_string_new("");
2667     uzbl.behave.html_endmarker = g_strdup(".");
2668     uzbl.behave.html_timeout = 60;
2669     uzbl.behave.base_url = g_strdup("http://invalid");
2670
2671     /* default mode indicators */
2672     uzbl.behave.insert_indicator = g_strdup("I");
2673     uzbl.behave.cmd_indicator    = g_strdup("C");
2674
2675     uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2676     uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2677     uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2678     uzbl.info.arch         = ARCH;
2679     uzbl.info.commit       = COMMIT;
2680
2681     commands_hash ();
2682     make_var_to_name_hash();
2683
2684     uzbl.gui.scrolled_win = create_browser();
2685 }
2686
2687 #ifndef UZBL_LIBRARY
2688 /** -- MAIN -- **/
2689 int
2690 main (int argc, char* argv[]) {
2691     initialize(argc, argv);
2692
2693     uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2694
2695     create_mainbar();
2696
2697     /* initial packing */
2698     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2699     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2700
2701     if (uzbl.state.socket_id) {
2702         uzbl.gui.plug = create_plug ();
2703         gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2704         gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2705     } else {
2706         uzbl.gui.main_window = create_window ();
2707         gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2708         gtk_widget_show_all (uzbl.gui.main_window);
2709         uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2710     }
2711
2712     if(!uzbl.state.instance_name)
2713         uzbl.state.instance_name = itos((int)uzbl.xwin);
2714
2715     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2716
2717     if (uzbl.state.verbose) {
2718         printf("Uzbl start location: %s\n", argv[0]);
2719         if (uzbl.state.socket_id)
2720             printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2721         else
2722             printf("window_id %i\n",(int) uzbl.xwin);
2723         printf("pid %i\n", getpid ());
2724         printf("name: %s\n", uzbl.state.instance_name);
2725     }
2726
2727     uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2728     uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2729     uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2730     uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2731     gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2732
2733     if(uzbl.gui.geometry)
2734         cmd_set_geometry();
2735     else
2736         retreive_geometry();
2737
2738     gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2739     if (argc > 1 && !uzbl.state.uri)
2740         uri_override = g_strdup(argv[1]);
2741     gboolean verbose_override = uzbl.state.verbose;
2742
2743     settings_init ();
2744     set_insert_mode(FALSE);
2745
2746     if (!uzbl.behave.show_status)
2747         gtk_widget_hide(uzbl.gui.mainbar);
2748     else
2749         update_title();
2750
2751     /* WebInspector */
2752     set_up_inspector();
2753
2754     if (verbose_override > uzbl.state.verbose)
2755         uzbl.state.verbose = verbose_override;
2756
2757     if (uri_override) {
2758         set_var_value("uri", uri_override);
2759         g_free(uri_override);
2760     } else if (uzbl.state.uri)
2761         cmd_load_uri(uzbl.gui.web_view, NULL);
2762
2763     gtk_main ();
2764     clean_up();
2765
2766     return EXIT_SUCCESS;
2767 }
2768 #endif
2769
2770 /* vi: set et ts=4: */