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