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