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