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