added more /*@null@*/ and const annotations
[uzbl-mobile] / uzbl.c
1 /* -*- c-basic-offset: 4; -*- */
2 // Original code taken from the example webkit-gtk+ application. see notice below.
3 // Modified code is licensed under the GPL 3.  See LICENSE file.
4
5
6 /*
7  * Copyright (C) 2006, 2007 Apple Inc.
8  * Copyright (C) 2007 Alp Toker <alp@atoker.com>
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
20  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32
33 #define LENGTH(x) (sizeof x / sizeof x[0])
34 #define _POSIX_SOURCE
35
36 #include <gtk/gtk.h>
37 #include <gdk/gdkx.h>
38 #include <gdk/gdkkeysyms.h>
39 #include <sys/socket.h>
40 #include <sys/stat.h>
41 #include <sys/types.h>
42 #include <sys/un.h>
43 #include <sys/utsname.h>
44 #include <sys/time.h>
45 #include <webkit/webkit.h>
46 #include <libsoup/soup.h>
47 #include <JavaScriptCore/JavaScript.h>
48
49 #include <stdio.h>
50 #include <string.h>
51 #include <unistd.h>
52 #include <stdlib.h>
53 #include <errno.h>
54 #include <fcntl.h>
55 #include <signal.h>
56 #include <assert.h>
57 #include "uzbl.h"
58 #include "config.h"
59
60 Uzbl uzbl;
61
62 /* commandline arguments (set initial values for the state variables) */
63 const
64 GOptionEntry entries[] =
65 {
66     { "uri",      'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
67         "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
68     { "verbose",  'v', 0, G_OPTION_ARG_NONE,   &uzbl.state.verbose,
69         "Whether to print all messages or just errors.", NULL },
70     { "name",     'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name,
71         "Name of the current instance (defaults to Xorg window id)", "NAME" },
72     { "config",   'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,
73         "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
74     { "socket",   's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,
75         "Socket ID", "SOCKET" },
76     { "geometry", 'g', 0, G_OPTION_ARG_STRING, &uzbl.gui.geometry,
77         "Set window geometry (format: WIDTHxHEIGHT+-X+-Y)", "GEOMETRY" },
78     { "version",  'V', 0, G_OPTION_ARG_NONE, &uzbl.behave.print_version,
79         "Print the version and exit", NULL },
80     { NULL,      0, 0, 0, NULL, NULL, NULL }
81 };
82
83 enum ptr_type {TYPE_INT, TYPE_STR, TYPE_FLOAT};
84
85 /* associate command names to their properties */
86 typedef const struct {
87     /* TODO: Make this ambiguous void **ptr into a union { char *char_p; int *int_p; float *float_p; } val;
88              the PTR() macro is kind of preventing this change at the moment. */
89     enum ptr_type type;
90     union {
91         int *i;
92         float *f;
93         gchar **s;
94     } ptr;
95     int dump;
96     int writeable;
97     /*@null@*/ void (*func)(void);
98 } uzbl_cmdprop;
99
100 /* abbreviations to help keep the table's width humane */
101 #define PTR_V_STR(var, d, fun) { .ptr.s = &(var), .type = TYPE_STR, .dump = d, .writeable = 1, .func = fun }
102 #define PTR_V_INT(var, d, fun) { .ptr.i = (int*)&(var), .type = TYPE_INT, .dump = d, .writeable = 1, .func = fun }
103 #define PTR_V_FLOAT(var, d, fun) { .ptr.f = &(var), .type = TYPE_FLOAT, .dump = d, .writeable = 1, .func = fun }
104 #define PTR_C_STR(var,    fun) { .ptr.s = &(var), .type = TYPE_STR, .dump = 0, .writeable = 0, .func = fun }
105 #define PTR_C_INT(var,    fun) { .ptr.i = (int*)&(var), .type = TYPE_INT, .dump = 0, .writeable = 0, .func = fun }
106 #define PTR_C_FLOAT(var,  fun) { .ptr.f = &(var), .type = TYPE_FLOAT, .dump = 0, .writeable = 0, .func = fun }
107
108 const struct var_name_to_ptr_t {
109     const char *name;
110     uzbl_cmdprop cp;
111 } var_name_to_ptr[] = {
112 /*    variable name         pointer to variable in code                  dump callback function    */
113 /*  ---------------------------------------------------------------------------------------------- */
114     { "uri",                 PTR_V_STR(uzbl.state.uri,                    1,   cmd_load_uri)},
115     { "verbose",             PTR_V_INT(uzbl.state.verbose,                1,   NULL)},
116     { "mode",                PTR_V_INT(uzbl.behave.mode,                  0,   NULL)},
117     { "inject_html",         PTR_V_STR(uzbl.behave.inject_html,           0,   cmd_inject_html)},
118     { "base_url",            PTR_V_STR(uzbl.behave.base_url,              1,   NULL)},
119     { "html_endmarker",      PTR_V_STR(uzbl.behave.html_endmarker,        1,   NULL)},
120     { "html_mode_timeout",   PTR_V_INT(uzbl.behave.html_timeout,          1,   NULL)},
121     { "keycmd",              PTR_V_STR(uzbl.state.keycmd,                 1,   set_keycmd)},
122     { "status_message",      PTR_V_STR(uzbl.gui.sbar.msg,                 1,   update_title)},
123     { "show_status",         PTR_V_INT(uzbl.behave.show_status,           1,   cmd_set_status)},
124     { "status_top",          PTR_V_INT(uzbl.behave.status_top,            1,   move_statusbar)},
125     { "status_format",       PTR_V_STR(uzbl.behave.status_format,         1,   update_title)},
126     { "status_pbar_done",    PTR_V_STR(uzbl.gui.sbar.progress_s,          1,   update_title)},
127     { "status_pbar_pending", PTR_V_STR(uzbl.gui.sbar.progress_u,          1,   update_title)},
128     { "status_pbar_width",   PTR_V_INT(uzbl.gui.sbar.progress_w,          1,   update_title)},
129     { "status_background",   PTR_V_STR(uzbl.behave.status_background,     1,   update_title)},
130     { "insert_indicator",    PTR_V_STR(uzbl.behave.insert_indicator,      1,   update_indicator)},
131     { "command_indicator",   PTR_V_STR(uzbl.behave.cmd_indicator,         1,   update_indicator)},
132     { "title_format_long",   PTR_V_STR(uzbl.behave.title_format_long,     1,   update_title)},
133     { "title_format_short",  PTR_V_STR(uzbl.behave.title_format_short,    1,   update_title)},
134     { "icon",                PTR_V_STR(uzbl.gui.icon,                     1,   set_icon)},
135     { "insert_mode",         PTR_V_INT(uzbl.behave.insert_mode,           1,   set_mode_indicator)},
136     { "always_insert_mode",  PTR_V_INT(uzbl.behave.always_insert_mode,    1,   cmd_always_insert_mode)},
137     { "reset_command_mode",  PTR_V_INT(uzbl.behave.reset_command_mode,    1,   NULL)},
138     { "modkey",              PTR_V_STR(uzbl.behave.modkey,                1,   cmd_modkey)},
139     { "load_finish_handler", PTR_V_STR(uzbl.behave.load_finish_handler,   1,   NULL)},
140     { "load_start_handler",  PTR_V_STR(uzbl.behave.load_start_handler,    1,   NULL)},
141     { "load_commit_handler", PTR_V_STR(uzbl.behave.load_commit_handler,   1,   NULL)},
142     { "history_handler",     PTR_V_STR(uzbl.behave.history_handler,       1,   NULL)},
143     { "download_handler",    PTR_V_STR(uzbl.behave.download_handler,      1,   NULL)},
144     { "cookie_handler",      PTR_V_STR(uzbl.behave.cookie_handler,        1,   cmd_cookie_handler)},
145     { "new_window",          PTR_V_STR(uzbl.behave.new_window,            1,   cmd_new_window)},
146     { "fifo_dir",            PTR_V_STR(uzbl.behave.fifo_dir,              1,   cmd_fifo_dir)},
147     { "socket_dir",          PTR_V_STR(uzbl.behave.socket_dir,            1,   cmd_socket_dir)},
148     { "http_debug",          PTR_V_INT(uzbl.behave.http_debug,            1,   cmd_http_debug)},
149     { "shell_cmd",           PTR_V_STR(uzbl.behave.shell_cmd,             1,   NULL)},
150     { "proxy_url",           PTR_V_STR(uzbl.net.proxy_url,                1,   set_proxy_url)},
151     { "max_conns",           PTR_V_INT(uzbl.net.max_conns,                1,   cmd_max_conns)},
152     { "max_conns_host",      PTR_V_INT(uzbl.net.max_conns_host,           1,   cmd_max_conns_host)},
153     { "useragent",           PTR_V_STR(uzbl.net.useragent,                1,   cmd_useragent)},
154
155     /* exported WebKitWebSettings properties */
156     { "zoom_level",          PTR_V_FLOAT(uzbl.behave.zoom_level,          1,   cmd_zoom_level)},
157     { "font_size",           PTR_V_INT(uzbl.behave.font_size,             1,   cmd_font_size)},
158     { "monospace_size",      PTR_V_INT(uzbl.behave.monospace_size,        1,   cmd_font_size)},
159     { "minimum_font_size",   PTR_V_INT(uzbl.behave.minimum_font_size,     1,   cmd_minimum_font_size)},
160     { "disable_plugins",     PTR_V_INT(uzbl.behave.disable_plugins,       1,   cmd_disable_plugins)},
161     { "disable_scripts",     PTR_V_INT(uzbl.behave.disable_scripts,       1,   cmd_disable_scripts)},
162     { "autoload_images",     PTR_V_INT(uzbl.behave.autoload_img,          1,   cmd_autoload_img)},
163     { "autoshrink_images",   PTR_V_INT(uzbl.behave.autoshrink_img,        1,   cmd_autoshrink_img)},
164     { "enable_spellcheck",   PTR_V_INT(uzbl.behave.enable_spellcheck,     1,   cmd_enable_spellcheck)},
165     { "enable_private",      PTR_V_INT(uzbl.behave.enable_private,        1,   cmd_enable_private)},
166     { "print_backgrounds",   PTR_V_INT(uzbl.behave.print_bg,              1,   cmd_print_bg)},
167     { "stylesheet_uri",      PTR_V_STR(uzbl.behave.style_uri,             1,   cmd_style_uri)},
168     { "resizable_text_areas",PTR_V_INT(uzbl.behave.resizable_txt,         1,   cmd_resizable_txt)},
169     { "default_encoding",    PTR_V_STR(uzbl.behave.default_encoding,      1,   cmd_default_encoding)},
170     { "enforce_96_dpi",      PTR_V_INT(uzbl.behave.enforce_96dpi,         1,   cmd_enforce_96dpi)},
171     { "caret_browsing",      PTR_V_INT(uzbl.behave.caret_browsing,        1,   cmd_caret_browsing)},
172
173   /* constants (not dumpable or writeable) */
174     { "WEBKIT_MAJOR",        PTR_C_INT(uzbl.info.webkit_major,                 NULL)},
175     { "WEBKIT_MINOR",        PTR_C_INT(uzbl.info.webkit_minor,                 NULL)},
176     { "WEBKIT_MICRO",        PTR_C_INT(uzbl.info.webkit_micro,                 NULL)},
177     { "ARCH_UZBL",           PTR_C_STR(uzbl.info.arch,                         NULL)},
178     { "COMMIT",              PTR_C_STR(uzbl.info.commit,                       NULL)},
179     { "LOAD_PROGRESS",       PTR_C_INT(uzbl.gui.sbar.load_progress,            NULL)},
180     { "LOAD_PROGRESSBAR",    PTR_C_STR(uzbl.gui.sbar.progress_bar,             NULL)},
181     { "TITLE",               PTR_C_STR(uzbl.gui.main_title,                    NULL)},
182     { "SELECTED_URI",        PTR_C_STR(uzbl.state.selected_url,                NULL)},
183     { "MODE",                PTR_C_STR(uzbl.gui.sbar.mode_indicator,           NULL)},
184     { "NAME",                PTR_C_STR(uzbl.state.instance_name,               NULL)},
185
186     { NULL,                  {.ptr.s = NULL, .type = TYPE_INT, .dump = 0, .writeable = 0, .func = NULL}}
187 };
188
189
190 const struct {
191     /*@null@*/ char *key;
192     guint mask;
193 } modkeys[] = {
194     { "SHIFT",   GDK_SHIFT_MASK   }, // shift
195     { "LOCK",    GDK_LOCK_MASK    }, // capslock or shiftlock, depending on xserver's modmappings
196     { "CONTROL", GDK_CONTROL_MASK }, // control
197     { "MOD1",    GDK_MOD1_MASK    }, // 4th mod - normally alt but depends on modmappings
198     { "MOD2",    GDK_MOD2_MASK    }, // 5th mod
199     { "MOD3",    GDK_MOD3_MASK    }, // 6th mod
200     { "MOD4",    GDK_MOD4_MASK    }, // 7th mod
201     { "MOD5",    GDK_MOD5_MASK    }, // 8th mod
202     { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
203     { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
204     { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
205     { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
206     { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
207     { "SUPER",   GDK_SUPER_MASK   }, // super (since 2.10)
208     { "HYPER",   GDK_HYPER_MASK   }, // hyper (since 2.10)
209     { "META",    GDK_META_MASK    }, // meta (since 2.10)
210     { NULL,      0                }
211 };
212
213
214 /* construct a hash from the var_name_to_ptr array for quick access */
215 void
216 make_var_to_name_hash() {
217     struct var_name_to_ptr_t *n2v_p = &var_name_to_ptr;
218     uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
219     while(n2v_p->name) {
220         g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
221         n2v_p++;
222     }
223 }
224
225 /* --- UTILITY FUNCTIONS --- */
226 enum exp_type {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS, EXP_ESCAPE};
227 enum exp_type
228 get_exp_type(const gchar *s) {
229     /* variables */
230     if(*(s+1) == '(')
231         return EXP_EXPR;
232     else if(*(s+1) == '{')
233         return EXP_BRACED_VAR;
234     else if(*(s+1) == '<')
235         return EXP_JS;
236     else if(*(s+1) == '[')
237         return EXP_ESCAPE;
238     else
239         return EXP_SIMPLE_VAR;
240
241     /*@notreached@*/
242 return EXP_ERR;
243 }
244
245 /*
246  * recurse == 1: don't expand '@(command)@'
247  * recurse == 2: don't expand '@<java script>@'
248  */
249 gchar *
250 expand(const char *s, guint recurse) {
251     uzbl_cmdprop *c;
252     enum exp_type etype;
253     char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
254     char *ret = NULL;
255     char *vend = NULL;
256     GError *err = NULL;
257     gchar *cmd_stdout = NULL;
258     gchar *mycmd = NULL;
259     GString *buf = g_string_new("");
260     GString *js_ret = g_string_new("");
261
262     while(*s) {
263         switch(*s) {
264             case '\\':
265                 g_string_append_c(buf, *++s);
266                 s++;
267                 break;
268
269             case '@':
270                 etype = get_exp_type(s);
271                 s++;
272
273                 switch(etype) {
274                     case EXP_SIMPLE_VAR:
275                         vend = strpbrk(s, end_simple_var);
276                         if(!vend) vend = strchr(s, '\0');
277                         break;
278                     case EXP_BRACED_VAR:
279                         s++;
280                         vend = strchr(s, '}');
281                         if(!vend) vend = strchr(s, '\0');
282                         break;
283                     case EXP_EXPR:
284                         s++;
285                         vend = strstr(s, ")@");
286                         if(!vend) vend = strchr(s, '\0');
287                         break;
288                     case EXP_JS:
289                         s++;
290                         vend = strstr(s, ">@");
291                         if(!vend) vend = strchr(s, '\0');
292                         break;
293                     case EXP_ESCAPE:
294                         s++;
295                         vend = strstr(s, "]@");
296                         if(!vend) vend = strchr(s, '\0');
297                         break;
298                 }
299                 assert(vend);
300
301                 ret = g_strndup(s, vend-s);
302
303                 if(etype == EXP_SIMPLE_VAR ||
304                    etype == EXP_BRACED_VAR) {
305                     if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
306                         if(c->type == TYPE_STR && *c->ptr.s != NULL) {
307                             g_string_append(buf, (gchar *)*c->ptr.s);
308                         } else if(c->type == TYPE_INT) {
309                             g_string_append_printf(buf, "%d", *c->ptr.i);
310                         }
311                         else if(c->type == TYPE_FLOAT) {
312                             g_string_append_printf(buf, "%f", *c->ptr.f);
313                         }
314                     }
315
316                     if(etype == EXP_SIMPLE_VAR)
317                         s = vend;
318                     else
319                         s = vend+1;
320                 }
321                 else if(recurse != 1 &&
322                         etype == EXP_EXPR) {
323                     mycmd = expand(ret, 1);
324                     g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
325                     g_free(mycmd);
326
327                     if (err) {
328                         g_printerr("error on running command: %s\n", err->message);
329                         g_error_free (err);
330                     }
331                     else if (*cmd_stdout) {
332                         size_t len = strlen(cmd_stdout);
333
334                         if(len > 0 && cmd_stdout[len-1] == '\n')
335                             cmd_stdout[--len] = '\0'; /* strip trailing newline */
336
337                         g_string_append(buf, cmd_stdout);
338                         g_free(cmd_stdout);
339                     }
340                     s = vend+2;
341                 }
342                 else if(recurse != 2 &&
343                         etype == EXP_JS) {
344                     mycmd = expand(ret, 2);
345                     eval_js(uzbl.gui.web_view, mycmd, js_ret);
346                     g_free(mycmd);
347
348                     if(js_ret->str) {
349                         g_string_append(buf, js_ret->str);
350                         g_string_free(js_ret, TRUE);
351                         js_ret = g_string_new("");
352                     }
353                     s = vend+2;
354                 }
355                 else if(etype == EXP_ESCAPE) {
356                     mycmd = expand(ret, 0);
357                     char *escaped = g_markup_escape_text(mycmd, strlen(mycmd));
358
359                     g_string_append(buf, escaped);
360
361                     g_free(escaped);
362                     g_free(mycmd);
363                     s = vend+2;
364                 }
365
366                 g_free(ret);
367                 ret = NULL;
368                 break;
369
370             default:
371                 g_string_append_c(buf, *s);
372                 s++;
373                 break;
374         }
375     }
376     g_string_free(js_ret, TRUE);
377     return g_string_free(buf, FALSE);
378 }
379
380 char *
381 itos(int val) {
382     char tmp[20];
383
384     snprintf(tmp, sizeof(tmp), "%i", val);
385     return g_strdup(tmp);
386 }
387
388 gchar*
389 strfree(gchar *str) { g_free(str); return NULL; }  // for freeing & setting to null in one go
390
391 gchar*
392 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
393
394 char *
395 str_replace (const char* search, const char* replace, const char* string) {
396     gchar **buf;
397     char *ret;
398
399     buf = g_strsplit (string, search, -1);
400     ret = g_strjoinv (replace, buf);
401     g_strfreev(buf); // somebody said this segfaults
402
403     return ret;
404 }
405
406 GArray*
407 read_file_by_line (const gchar *path) {
408     GIOChannel *chan = NULL;
409     gchar *readbuf = NULL;
410     gsize len;
411     GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
412     int i = 0;
413
414     chan = g_io_channel_new_file(path, "r", NULL);
415
416     if (chan) {
417         while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
418             const gchar* val = g_strdup (readbuf);
419             g_array_append_val (lines, val);
420             g_free (readbuf);
421             i ++;
422         }
423
424         g_io_channel_unref (chan);
425     } else {
426         fprintf(stderr, "File '%s' not be read.\n", path);
427     }
428
429     return lines;
430 }
431
432 gchar*
433 parseenv (char* string) {
434     extern char** environ;
435     gchar* tmpstr = NULL;
436     int i = 0;
437
438
439     while (environ[i] != NULL) {
440         gchar** env = g_strsplit (environ[i], "=", 2);
441         gchar* envname = g_strconcat ("$", env[0], NULL);
442
443         if (g_strrstr (string, envname) != NULL) {
444             tmpstr = g_strdup(string);
445             g_free (string);
446             string = str_replace(envname, env[1], tmpstr);
447             g_free (tmpstr);
448         }
449
450         g_free (envname);
451         g_strfreev (env); // somebody said this breaks uzbl
452         i++;
453     }
454
455     return string;
456 }
457
458 sigfunc*
459 setup_signal(int signr, sigfunc *shandler) {
460     struct sigaction nh, oh;
461
462     nh.sa_handler = shandler;
463     sigemptyset(&nh.sa_mask);
464     nh.sa_flags = 0;
465
466     if(sigaction(signr, &nh, &oh) < 0)
467         return SIG_ERR;
468
469     return NULL;
470 }
471
472 void
473 clean_up(void) {
474     if (uzbl.behave.fifo_dir)
475         unlink (uzbl.comm.fifo_path);
476     if (uzbl.behave.socket_dir)
477         unlink (uzbl.comm.socket_path);
478
479     g_free(uzbl.state.executable_path);
480     g_free(uzbl.state.keycmd);
481     g_hash_table_destroy(uzbl.bindings);
482     g_hash_table_destroy(uzbl.behave.commands);
483 }
484
485 /* used for html_mode_timeout
486  * be sure to extend this function to use
487  * more timers if needed in other places
488 */
489 void
490 set_timeout(int seconds) {
491     struct itimerval t;
492     memset(&t, 0, sizeof t);
493
494     t.it_value.tv_sec =  seconds;
495     t.it_value.tv_usec = 0;
496     setitimer(ITIMER_REAL, &t, NULL);
497 }
498
499 /* --- SIGNAL HANDLER --- */
500
501 void
502 catch_sigterm(int s) {
503     (void) s;
504     clean_up();
505 }
506
507 void
508 catch_sigint(int s) {
509     (void) s;
510     clean_up();
511     exit(EXIT_SUCCESS);
512 }
513
514 void
515 catch_alrm(int s) {
516     (void) s;
517
518     set_var_value("mode", "0");
519     render_html();
520 }
521
522
523 /* --- CALLBACKS --- */
524
525 gboolean
526 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
527     (void) web_view;
528     (void) frame;
529     (void) navigation_action;
530     (void) policy_decision;
531     (void) user_data;
532     const gchar* uri = webkit_network_request_get_uri (request);
533     if (uzbl.state.verbose)
534         printf("New window requested -> %s \n", uri);
535     webkit_web_policy_decision_use(policy_decision);
536     return TRUE;
537 }
538
539 gboolean
540 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type,  WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
541     (void) frame;
542     (void) request;
543     (void) user_data;
544
545     /* If we can display it, let's display it... */
546     if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
547         webkit_web_policy_decision_use (policy_decision);
548         return TRUE;
549     }
550
551     /* ...everything we can't displayed is downloaded */
552     webkit_web_policy_decision_download (policy_decision);
553     return TRUE;
554 }
555
556 /*@null@*/ WebKitWebView*
557 create_web_view_cb (WebKitWebView  *web_view, WebKitWebFrame *frame, gpointer user_data) {
558     (void) web_view;
559     (void) frame;
560     (void) user_data;
561     if (uzbl.state.selected_url != NULL) {
562         if (uzbl.state.verbose)
563             printf("\nNew web view -> %s\n",uzbl.state.selected_url);
564         new_window_load_uri(uzbl.state.selected_url);
565     } else {
566         if (uzbl.state.verbose)
567             printf("New web view -> %s\n","Nothing to open, exiting");
568     }
569     return (NULL);
570 }
571
572 gboolean
573 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
574     (void) web_view;
575     (void) user_data;
576     if (uzbl.behave.download_handler) {
577         const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
578         if (uzbl.state.verbose)
579             printf("Download -> %s\n",uri);
580         /* if urls not escaped, we may have to escape and quote uri before this call */
581         run_handler(uzbl.behave.download_handler, uri);
582     }
583     return (FALSE);
584 }
585
586 /* scroll a bar in a given direction */
587 void
588 scroll (GtkAdjustment* bar, GArray *argv) {
589     gchar *end;
590     gdouble max_value;
591
592     gdouble page_size = gtk_adjustment_get_page_size(bar);
593     gdouble value = gtk_adjustment_get_value(bar);
594     gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
595
596     if (*end == '%')
597         value += page_size * amount * 0.01;
598     else
599         value += amount;
600
601     max_value = gtk_adjustment_get_upper(bar) - page_size;
602
603     if (value > max_value)
604         value = max_value; /* don't scroll past the end of the page */
605
606     gtk_adjustment_set_value (bar, value);
607 }
608
609 void
610 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
611     (void) page; (void) argv; (void) result;
612     gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
613 }
614
615 void
616 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
617     (void) page; (void) argv; (void) result;
618     gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
619                               gtk_adjustment_get_page_size(uzbl.gui.bar_v));
620 }
621
622 void
623 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
624     (void) page; (void) result;
625     scroll(uzbl.gui.bar_v, argv);
626 }
627
628 void
629 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
630     (void) page; (void) result;
631     scroll(uzbl.gui.bar_h, argv);
632 }
633
634 void
635 cmd_set_geometry() {
636     if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
637         if(uzbl.state.verbose)
638             printf("Error in geometry string: %s\n", uzbl.gui.geometry);
639     }
640     /* update geometry var with the actual geometry
641        this is necessary as some WMs don't seem to honour
642        the above setting and we don't want to end up with
643        wrong geometry information
644     */
645     retreive_geometry();
646 }
647
648 void
649 cmd_set_status() {
650     if (!uzbl.behave.show_status) {
651         gtk_widget_hide(uzbl.gui.mainbar);
652     } else {
653         gtk_widget_show(uzbl.gui.mainbar);
654     }
655     update_title();
656 }
657
658 void
659 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
660     (void)page;
661     (void)argv;
662     (void)result;
663
664     webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
665 }
666
667 void
668 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
669     (void)page;
670     (void)argv;
671     (void)result;
672
673     if (uzbl.behave.show_status) {
674         gtk_widget_hide(uzbl.gui.mainbar);
675     } else {
676         gtk_widget_show(uzbl.gui.mainbar);
677     }
678     uzbl.behave.show_status = !uzbl.behave.show_status;
679     update_title();
680 }
681
682 void
683 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
684     (void) page;
685     (void) title;
686     (void) data;
687     //Set selected_url state variable
688     g_free(uzbl.state.selected_url);
689     uzbl.state.selected_url = NULL;
690     if (link) {
691         uzbl.state.selected_url = g_strdup(link);
692     }
693     update_title();
694 }
695
696 void
697 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
698     (void) web_view;
699     (void) param_spec;
700     const gchar *title = webkit_web_view_get_title(web_view);
701     if (uzbl.gui.main_title)
702         g_free (uzbl.gui.main_title);
703     uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
704     update_title();
705 }
706
707 void
708 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
709     (void) page;
710     (void) data;
711     uzbl.gui.sbar.load_progress = progress;
712
713     g_free(uzbl.gui.sbar.progress_bar);
714     uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
715
716     update_title();
717 }
718
719 void
720 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
721     (void) page;
722     (void) frame;
723     (void) data;
724     if (uzbl.behave.load_finish_handler)
725         run_handler(uzbl.behave.load_finish_handler, "");
726 }
727
728 void clear_keycmd() {
729   g_free(uzbl.state.keycmd);
730   uzbl.state.keycmd = g_strdup("");
731 }
732
733 void
734 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
735     (void) page;
736     (void) frame;
737     (void) data;
738     uzbl.gui.sbar.load_progress = 0;
739     clear_keycmd(); // don't need old commands to remain on new page?
740     if (uzbl.behave.load_start_handler)
741         run_handler(uzbl.behave.load_start_handler, "");
742 }
743
744 void
745 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
746     (void) page;
747     (void) data;
748     g_free (uzbl.state.uri);
749     GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
750     uzbl.state.uri = g_string_free (newuri, FALSE);
751     if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
752         set_insert_mode(uzbl.behave.always_insert_mode);
753         update_title();
754     }
755     if (uzbl.behave.load_commit_handler)
756         run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
757 }
758
759 void
760 destroy_cb (GtkWidget* widget, gpointer data) {
761     (void) widget;
762     (void) data;
763     gtk_main_quit ();
764 }
765
766 void
767 log_history_cb () {
768    if (uzbl.behave.history_handler) {
769        time_t rawtime;
770        struct tm * timeinfo;
771        char date [80];
772        time ( &rawtime );
773        timeinfo = localtime ( &rawtime );
774        strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
775        run_handler(uzbl.behave.history_handler, date);
776    }
777 }
778
779
780 /* VIEW funcs (little webkit wrappers) */
781 #define VIEWFUNC(name) void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
782 VIEWFUNC(reload)
783 VIEWFUNC(reload_bypass_cache)
784 VIEWFUNC(stop_loading)
785 VIEWFUNC(zoom_in)
786 VIEWFUNC(zoom_out)
787 VIEWFUNC(go_back)
788 VIEWFUNC(go_forward)
789 #undef VIEWFUNC
790
791 /* -- command to callback/function map for things we cannot attach to any signals */
792 struct {const char *key; CommandInfo value;} cmdlist[] =
793 {   /* key                   function      no_split      */
794     { "back",               {view_go_back, 0}              },
795     { "forward",            {view_go_forward, 0}           },
796     { "scroll_vert",        {scroll_vert, 0}               },
797     { "scroll_horz",        {scroll_horz, 0}               },
798     { "scroll_begin",       {scroll_begin, 0}              },
799     { "scroll_end",         {scroll_end, 0}                },
800     { "reload",             {view_reload, 0},              },
801     { "reload_ign_cache",   {view_reload_bypass_cache, 0}  },
802     { "stop",               {view_stop_loading, 0},        },
803     { "zoom_in",            {view_zoom_in, 0},             }, //Can crash (when max zoom reached?).
804     { "zoom_out",           {view_zoom_out, 0},            },
805     { "toggle_zoom_type",   {toggle_zoom_type, 0},         },
806     { "uri",                {load_uri, TRUE}               },
807     { "js",                 {run_js, TRUE}                 },
808     { "script",             {run_external_js, 0}           },
809     { "toggle_status",      {toggle_status_cb, 0}          },
810     { "spawn",              {spawn, 0}                     },
811     { "sync_spawn",         {spawn_sync, 0}                }, // needed for cookie handler
812     { "sh",                 {spawn_sh, 0}                  },
813     { "sync_sh",            {spawn_sh_sync, 0}             }, // needed for cookie handler
814     { "exit",               {close_uzbl, 0}                },
815     { "search",             {search_forward_text, TRUE}    },
816     { "search_reverse",     {search_reverse_text, TRUE}    },
817     { "dehilight",          {dehilight, 0}                 },
818     { "toggle_insert_mode", {toggle_insert_mode, 0}        },
819     { "set",                {set_var, TRUE}                },
820   //{ "get",                {get_var, TRUE}                },
821     { "bind",               {act_bind, TRUE}               },
822     { "dump_config",        {act_dump_config, 0}           },
823     { "keycmd",             {keycmd, TRUE}                 },
824     { "keycmd_nl",          {keycmd_nl, TRUE}              },
825     { "keycmd_bs",          {keycmd_bs, 0}                 },
826     { "chain",              {chain, 0}                     },
827     { "print",              {print, TRUE}                  }
828 };
829
830 void
831 commands_hash(void)
832 {
833     unsigned int i;
834     uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
835
836     for (i = 0; i < LENGTH(cmdlist); i++)
837         g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
838 }
839
840 /* -- CORE FUNCTIONS -- */
841
842 void
843 free_action(gpointer act) {
844     Action *action = (Action*)act;
845     g_free(action->name);
846     if (action->param)
847         g_free(action->param);
848     g_free(action);
849 }
850
851 Action*
852 new_action(const gchar *name, const gchar *param) {
853     Action *action = g_new(Action, 1);
854
855     action->name = g_strdup(name);
856     if (param)
857         action->param = g_strdup(param);
858     else
859         action->param = NULL;
860
861     return action;
862 }
863
864 bool
865 file_exists (const char * filename) {
866     return (access(filename, F_OK) == 0);
867 }
868
869 void
870 set_var(WebKitWebView *page, GArray *argv, GString *result) {
871     (void) page; (void) result;
872     gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
873     if (split[0] != NULL) {
874         gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
875         set_var_value(g_strstrip(split[0]), value);
876         g_free(value);
877     }
878     g_strfreev(split);
879 }
880
881 void
882 print(WebKitWebView *page, GArray *argv, GString *result) {
883     (void) page; (void) result;
884     gchar* buf;
885
886     buf = expand(argv_idx(argv, 0), 0);
887     g_string_assign(result, buf);
888     g_free(buf);
889 }
890
891 void
892 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
893     (void) page; (void) result;
894     gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
895     gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
896     add_binding(g_strstrip(split[0]), value);
897     g_free(value);
898     g_strfreev(split);
899 }
900
901
902 void
903 act_dump_config() {
904     dump_config();
905 }
906
907 void
908 set_keycmd() {
909     run_keycmd(FALSE);
910     update_title();
911 }
912
913 void
914 set_mode_indicator() {
915     uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
916         uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
917 }
918
919 void
920 update_indicator() {
921   set_mode_indicator();
922   update_title();
923 }
924
925 void
926 set_insert_mode(gboolean mode) {
927     uzbl.behave.insert_mode = mode;
928     set_mode_indicator();
929 }
930
931 void
932 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
933     (void) page; (void) result;
934
935     if (argv_idx(argv, 0)) {
936         if (strcmp (argv_idx(argv, 0), "0") == 0) {
937             set_insert_mode(FALSE);
938         } else {
939             set_insert_mode(TRUE);
940         }
941     } else {
942         set_insert_mode( !uzbl.behave.insert_mode );
943     }
944
945     update_title();
946 }
947
948 void
949 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
950     (void) result;
951
952     if (argv_idx(argv, 0)) {
953         GString* newuri = g_string_new (argv_idx(argv, 0));
954         if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
955             run_js(web_view, argv, NULL);
956             return;
957         }
958         if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
959             g_string_prepend (newuri, "http://");
960         /* if we do handle cookies, ask our handler for them */
961         webkit_web_view_load_uri (web_view, newuri->str);
962         g_string_free (newuri, TRUE);
963     }
964 }
965
966 /* Javascript*/
967
968 JSValueRef
969 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
970                 size_t argumentCount, const JSValueRef arguments[],
971                 JSValueRef* exception) {
972     (void) function;
973     (void) thisObject;
974     (void) exception;
975
976     JSStringRef js_result_string;
977     GString *result = g_string_new("");
978
979     if (argumentCount >= 1) {
980         JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
981         size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
982         char ctl_line[arg_size];
983         JSStringGetUTF8CString(arg, ctl_line, arg_size);
984
985         parse_cmd_line(ctl_line, result);
986
987         JSStringRelease(arg);
988     }
989     js_result_string = JSStringCreateWithUTF8CString(result->str);
990
991     g_string_free(result, TRUE);
992
993     return JSValueMakeString(ctx, js_result_string);
994 }
995
996 JSStaticFunction js_static_functions[] = {
997     {"run", js_run_command, kJSPropertyAttributeNone},
998 };
999
1000 void
1001 js_init() {
1002     /* This function creates the class and its definition, only once */
1003     if (!uzbl.js.initialized) {
1004         /* it would be pretty cool to make this dynamic */
1005         uzbl.js.classdef = kJSClassDefinitionEmpty;
1006         uzbl.js.classdef.staticFunctions = js_static_functions;
1007
1008         uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
1009     }
1010 }
1011
1012
1013 void
1014 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
1015     WebKitWebFrame *frame;
1016     JSGlobalContextRef context;
1017     JSObjectRef globalobject;
1018     JSStringRef var_name;
1019
1020     JSStringRef js_script;
1021     JSValueRef js_result;
1022     JSStringRef js_result_string;
1023     size_t js_result_size;
1024
1025     js_init();
1026
1027     frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
1028     context = webkit_web_frame_get_global_context(frame);
1029     globalobject = JSContextGetGlobalObject(context);
1030
1031     /* uzbl javascript namespace */
1032     var_name = JSStringCreateWithUTF8CString("Uzbl");
1033     JSObjectSetProperty(context, globalobject, var_name,
1034                         JSObjectMake(context, uzbl.js.classref, NULL),
1035                         kJSClassAttributeNone, NULL);
1036
1037     /* evaluate the script and get return value*/
1038     js_script = JSStringCreateWithUTF8CString(script);
1039     js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1040     if (js_result && !JSValueIsUndefined(context, js_result)) {
1041         js_result_string = JSValueToStringCopy(context, js_result, NULL);
1042         js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1043
1044         if (js_result_size) {
1045             char js_result_utf8[js_result_size];
1046             JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1047             g_string_assign(result, js_result_utf8);
1048         }
1049
1050         JSStringRelease(js_result_string);
1051     }
1052
1053     /* cleanup */
1054     JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1055
1056     JSStringRelease(var_name);
1057     JSStringRelease(js_script);
1058 }
1059
1060 void
1061 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1062     if (argv_idx(argv, 0))
1063         eval_js(web_view, argv_idx(argv, 0), result);
1064 }
1065
1066 void
1067 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1068     (void) result;
1069     if (argv_idx(argv, 0)) {
1070         GArray* lines = read_file_by_line (argv_idx (argv, 0));
1071         gchar*  js = NULL;
1072         int i = 0;
1073         gchar* line;
1074
1075         while ((line = g_array_index(lines, gchar*, i))) {
1076             if (js == NULL) {
1077                 js = g_strdup (line);
1078             } else {
1079                 gchar* newjs = g_strconcat (js, line, NULL);
1080                 js = newjs;
1081             }
1082             i ++;
1083             g_free (line);
1084         }
1085
1086         if (uzbl.state.verbose)
1087             printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1088
1089         if (argv_idx (argv, 1)) {
1090             gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1091             g_free (js);
1092             js = newjs;
1093         }
1094         eval_js (web_view, js, result);
1095         g_free (js);
1096         g_array_free (lines, TRUE);
1097     }
1098 }
1099
1100 void
1101 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1102     if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1103         if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1104             webkit_web_view_unmark_text_matches (page);
1105             webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1106             uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1107         }
1108     }
1109
1110     if (uzbl.state.searchtx) {
1111         if (uzbl.state.verbose)
1112             printf ("Searching: %s\n", uzbl.state.searchtx);
1113         webkit_web_view_set_highlight_text_matches (page, TRUE);
1114         webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1115     }
1116 }
1117
1118 void
1119 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1120     (void) result;
1121     search_text(page, argv, TRUE);
1122 }
1123
1124 void
1125 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1126     (void) result;
1127     search_text(page, argv, FALSE);
1128 }
1129
1130 void
1131 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1132     (void) argv; (void) result;
1133     webkit_web_view_set_highlight_text_matches (page, FALSE);
1134 }
1135
1136
1137 void
1138 new_window_load_uri (const gchar * uri) {
1139     if (uzbl.behave.new_window) {
1140         GString *s = g_string_new ("");
1141         g_string_printf(s, "'%s'", uri);
1142         run_handler(uzbl.behave.new_window, s->str);
1143         return;
1144     }
1145     GString* to_execute = g_string_new ("");
1146     g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1147     int i;
1148     for (i = 0; entries[i].long_name != NULL; i++) {
1149         if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1150             gchar** str = (gchar**)entries[i].arg_data;
1151             if (*str!=NULL) {
1152                 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1153             }
1154         }
1155     }
1156     if (uzbl.state.verbose)
1157         printf("\n%s\n", to_execute->str);
1158     g_spawn_command_line_async (to_execute->str, NULL);
1159     g_string_free (to_execute, TRUE);
1160 }
1161
1162 void
1163 chain (WebKitWebView *page, GArray *argv, GString *result) {
1164     (void) page; (void) result;
1165     gchar *a = NULL;
1166     gchar **parts = NULL;
1167     guint i = 0;
1168     while ((a = argv_idx(argv, i++))) {
1169         parts = g_strsplit (a, " ", 2);
1170         if (parts[0])
1171           parse_command(parts[0], parts[1], result);
1172         g_strfreev (parts);
1173     }
1174 }
1175
1176 void
1177 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1178     (void)page;
1179     (void)argv;
1180     (void)result;
1181     uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1182     run_keycmd(FALSE);
1183     update_title();
1184 }
1185
1186 void
1187 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1188     (void)page;
1189     (void)argv;
1190     (void)result;
1191     uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1192     run_keycmd(TRUE);
1193     update_title();
1194 }
1195
1196 void
1197 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1198     gchar *prev;
1199     (void)page;
1200     (void)argv;
1201     (void)result;
1202     int len = strlen(uzbl.state.keycmd);
1203     prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1204     if (prev)
1205       uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1206     update_title();
1207 }
1208
1209 void
1210 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1211     (void)page;
1212     (void)argv;
1213     (void)result;
1214     gtk_main_quit ();
1215 }
1216
1217 /* --Statusbar functions-- */
1218 char*
1219 build_progressbar_ascii(int percent) {
1220    int width=uzbl.gui.sbar.progress_w;
1221    int i;
1222    double l;
1223    GString *bar = g_string_new("");
1224
1225    l = (double)percent*((double)width/100.);
1226    l = (int)(l+.5)>=(int)l ? l+.5 : l;
1227
1228    for(i=0; i<(int)l; i++)
1229        g_string_append(bar, uzbl.gui.sbar.progress_s);
1230
1231    for(; i<width; i++)
1232        g_string_append(bar, uzbl.gui.sbar.progress_u);
1233
1234    return g_string_free(bar, FALSE);
1235 }
1236 /* --End Statusbar functions-- */
1237
1238 void
1239 sharg_append(GArray *a, const gchar *str) {
1240     const gchar *s = (str ? str : "");
1241     g_array_append_val(a, s);
1242 }
1243
1244 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1245 gboolean
1246 run_command (const gchar *command, const guint npre, const gchar **args,
1247              const gboolean sync, char **output_stdout) {
1248    //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1249     GError *err = NULL;
1250
1251     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1252     gchar *pid = itos(getpid());
1253     gchar *xwin = itos(uzbl.xwin);
1254     guint i;
1255     sharg_append(a, command);
1256     for (i = 0; i < npre; i++) /* add n args before the default vars */
1257         sharg_append(a, args[i]);
1258     sharg_append(a, uzbl.state.config_file);
1259     sharg_append(a, pid);
1260     sharg_append(a, xwin);
1261     sharg_append(a, uzbl.comm.fifo_path);
1262     sharg_append(a, uzbl.comm.socket_path);
1263     sharg_append(a, uzbl.state.uri);
1264     sharg_append(a, uzbl.gui.main_title);
1265
1266     for (i = npre; i < g_strv_length((gchar**)args); i++)
1267         sharg_append(a, args[i]);
1268
1269     gboolean result;
1270     if (sync) {
1271         if (*output_stdout) *output_stdout = strfree(*output_stdout);
1272
1273         result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1274                               NULL, NULL, output_stdout, NULL, NULL, &err);
1275     } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1276                                   NULL, NULL, NULL, &err);
1277
1278     if (uzbl.state.verbose) {
1279         GString *s = g_string_new("spawned:");
1280         for (i = 0; i < (a->len); i++) {
1281             gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1282             g_string_append_printf(s, " %s", qarg);
1283             g_free (qarg);
1284         }
1285         g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1286         printf("%s\n", s->str);
1287         g_string_free(s, TRUE);
1288         if(output_stdout) {
1289             printf("Stdout: %s\n", *output_stdout);
1290         }
1291     }
1292     if (err) {
1293         g_printerr("error on run_command: %s\n", err->message);
1294         g_error_free (err);
1295     }
1296     g_free (pid);
1297     g_free (xwin);
1298     g_array_free (a, TRUE);
1299     return result;
1300 }
1301
1302 /*@null@*/ gchar**
1303 split_quoted(const gchar* src, const gboolean unquote) {
1304     /* split on unquoted space, return array of strings;
1305        remove a layer of quotes and backslashes if unquote */
1306     if (!src) return NULL;
1307
1308     gboolean dq = FALSE;
1309     gboolean sq = FALSE;
1310     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1311     GString *s = g_string_new ("");
1312     const gchar *p;
1313     gchar **ret;
1314     gchar *dup;
1315     for (p = src; *p != '\0'; p++) {
1316         if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1317         else if (*p == '\\') { g_string_append_c(s, *p++);
1318                                g_string_append_c(s, *p); }
1319         else if ((*p == '"') && unquote && !sq) dq = !dq;
1320         else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1321                                      dq = !dq; }
1322         else if ((*p == '\'') && unquote && !dq) sq = !sq;
1323         else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1324                                       sq = ! sq; }
1325         else if ((*p == ' ') && !dq && !sq) {
1326             dup = g_strdup(s->str);
1327             g_array_append_val(a, dup);
1328             g_string_truncate(s, 0);
1329         } else g_string_append_c(s, *p);
1330     }
1331     dup = g_strdup(s->str);
1332     g_array_append_val(a, dup);
1333     ret = (gchar**)a->data;
1334     g_array_free (a, FALSE);
1335     g_string_free (s, TRUE);
1336     return ret;
1337 }
1338
1339 void
1340 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1341     (void)web_view; (void)result;
1342     //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1343     if (argv_idx(argv, 0))
1344         run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1345 }
1346
1347 void
1348 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1349     (void)web_view; (void)result;
1350
1351     if (argv_idx(argv, 0))
1352         run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1353                     TRUE, &uzbl.comm.sync_stdout);
1354 }
1355
1356 void
1357 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1358     (void)web_view; (void)result;
1359     if (!uzbl.behave.shell_cmd) {
1360         g_printerr ("spawn_sh: shell_cmd is not set!\n");
1361         return;
1362     }
1363
1364     guint i;
1365     gchar *spacer = g_strdup("");
1366     g_array_insert_val(argv, 1, spacer);
1367     gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1368
1369     for (i = 1; i < g_strv_length(cmd); i++)
1370         g_array_prepend_val(argv, cmd[i]);
1371
1372     if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1373     g_free (spacer);
1374     g_strfreev (cmd);
1375 }
1376
1377 void
1378 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1379     (void)web_view; (void)result;
1380     if (!uzbl.behave.shell_cmd) {
1381         g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1382         return;
1383     }
1384
1385     guint i;
1386     gchar *spacer = g_strdup("");
1387     g_array_insert_val(argv, 1, spacer);
1388     gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1389
1390     for (i = 1; i < g_strv_length(cmd); i++)
1391         g_array_prepend_val(argv, cmd[i]);
1392
1393     if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1394                          TRUE, &uzbl.comm.sync_stdout);
1395     g_free (spacer);
1396     g_strfreev (cmd);
1397 }
1398
1399 void
1400 parse_command(const char *cmd, const char *param, GString *result) {
1401     CommandInfo *c;
1402
1403     if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1404             guint i;
1405             gchar **par = split_quoted(param, TRUE);
1406             GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1407
1408             if (c->no_split) { /* don't split */
1409                 sharg_append(a, param);
1410             } else if (par) {
1411                 for (i = 0; i < g_strv_length(par); i++)
1412                     sharg_append(a, par[i]);
1413             }
1414
1415             if (result == NULL) {
1416                 GString *result_print = g_string_new("");
1417
1418                 c->function(uzbl.gui.web_view, a, result_print);
1419                 if (result_print->len)
1420                     printf("%*s\n", (int)result_print->len, result_print->str);
1421
1422                 g_string_free(result_print, TRUE);
1423             } else {
1424                 c->function(uzbl.gui.web_view, a, result);
1425             }
1426             g_strfreev (par);
1427             g_array_free (a, TRUE);
1428
1429     } else
1430         g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1431 }
1432
1433 void
1434 set_proxy_url() {
1435     SoupURI *suri;
1436
1437     if(uzbl.net.proxy_url == NULL || *uzbl.net.proxy_url == ' ') {
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(const 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 /*@null@*/ 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 /*@null@*/ 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 /*@null@*/ 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 /*@null@*/ 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 /*@null@*/ gchar*
2379 find_xdg_file (int xdg_type, const 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         g_free(temporary_file);
2409         return NULL;
2410     }
2411 }
2412 void
2413 settings_init () {
2414     State *s = &uzbl.state;
2415     Network *n = &uzbl.net;
2416     int i;
2417     for (i = 0; default_config[i].command != NULL; i++) {
2418         parse_cmd_line(default_config[i].command, NULL);
2419     }
2420
2421     if (g_strcmp0(s->config_file, "-") == 0) {
2422         s->config_file = NULL;
2423         create_stdin();
2424     }
2425
2426     else if (!s->config_file) {
2427         s->config_file = find_xdg_file (0, "/uzbl/config");
2428     }
2429
2430     if (s->config_file) {
2431         GArray* lines = read_file_by_line (s->config_file);
2432         int i = 0;
2433         gchar* line;
2434
2435         while ((line = g_array_index(lines, gchar*, i))) {
2436             parse_cmd_line (line, NULL);
2437             i ++;
2438             g_free (line);
2439         }
2440         g_array_free (lines, TRUE);
2441     } else {
2442         if (uzbl.state.verbose)
2443             printf ("No configuration file loaded.\n");
2444     }
2445
2446     g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2447 }
2448
2449 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2450     (void) session;
2451     (void) user_data;
2452     if (!uzbl.behave.cookie_handler)
2453          return;
2454
2455     soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2456     GString *s = g_string_new ("");
2457     SoupURI * soup_uri = soup_message_get_uri(msg);
2458     g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2459     run_handler(uzbl.behave.cookie_handler, s->str);
2460
2461     if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2462         char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2463         if ( p != NULL ) *p = '\0';
2464         soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2465     }
2466     if (uzbl.comm.sync_stdout)
2467         uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2468
2469     g_string_free(s, TRUE);
2470 }
2471
2472 void
2473 save_cookies (SoupMessage *msg, gpointer user_data){
2474     (void) user_data;
2475     GSList *ck;
2476     char *cookie;
2477     for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2478         cookie = soup_cookie_to_set_cookie_header(ck->data);
2479         SoupURI * soup_uri = soup_message_get_uri(msg);
2480         GString *s = g_string_new ("");
2481         g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2482         run_handler(uzbl.behave.cookie_handler, s->str);
2483         g_free (cookie);
2484         g_string_free(s, TRUE);
2485     }
2486     g_slist_free(ck);
2487 }
2488
2489 /* --- WEBINSPECTOR --- */
2490 void
2491 hide_window_cb(GtkWidget *widget, gpointer data) {
2492     (void) data;
2493
2494     gtk_widget_hide(widget);
2495 }
2496
2497 WebKitWebView*
2498 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2499     (void) data;
2500     (void) page;
2501     (void) web_inspector;
2502     GtkWidget* scrolled_window;
2503     GtkWidget* new_web_view;
2504     GUI *g = &uzbl.gui;
2505
2506     g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2507     g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2508             G_CALLBACK(hide_window_cb), NULL);
2509
2510     gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2511     gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2512     gtk_widget_show(g->inspector_window);
2513
2514     scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2515     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2516             GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2517     gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2518     gtk_widget_show(scrolled_window);
2519
2520     new_web_view = webkit_web_view_new();
2521     gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2522
2523     return WEBKIT_WEB_VIEW(new_web_view);
2524 }
2525
2526 gboolean
2527 inspector_show_window_cb (WebKitWebInspector* inspector){
2528     (void) inspector;
2529     gtk_widget_show(uzbl.gui.inspector_window);
2530     return TRUE;
2531 }
2532
2533 /* TODO: Add variables and code to make use of these functions */
2534 gboolean
2535 inspector_close_window_cb (WebKitWebInspector* inspector){
2536     (void) inspector;
2537     return TRUE;
2538 }
2539
2540 gboolean
2541 inspector_attach_window_cb (WebKitWebInspector* inspector){
2542     (void) inspector;
2543     return FALSE;
2544 }
2545
2546 gboolean
2547 inspector_detach_window_cb (WebKitWebInspector* inspector){
2548     (void) inspector;
2549     return FALSE;
2550 }
2551
2552 gboolean
2553 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2554     (void) inspector;
2555     return FALSE;
2556 }
2557
2558 gboolean
2559 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2560     (void) inspector;
2561     return FALSE;
2562 }
2563
2564 void
2565 set_up_inspector() {
2566     GUI *g = &uzbl.gui;
2567     WebKitWebSettings *settings = view_settings();
2568     g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2569
2570     uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2571     g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2572     g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2573     g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2574     g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2575     g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2576     g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2577
2578     g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2579 }
2580
2581 void
2582 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2583     (void) ud;
2584     uzbl_cmdprop *c = v;
2585
2586     if(!c->dump)
2587         return;
2588
2589     if(c->type == TYPE_STR)
2590         printf("set %s = %s\n", (char *)k, *c->ptr.s ? *c->ptr.s : " ");
2591     else if(c->type == TYPE_INT)
2592         printf("set %s = %d\n", (char *)k, *c->ptr.i);
2593     else if(c->type == TYPE_FLOAT)
2594         printf("set %s = %f\n", (char *)k, *c->ptr.f);
2595 }
2596
2597 void
2598 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2599     (void) ud;
2600     Action *a = v;
2601
2602     printf("bind %s = %s %s\n", (char *)k ,
2603             (char *)a->name, a->param?(char *)a->param:"");
2604 }
2605
2606 void
2607 dump_config() {
2608     g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2609     g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2610 }
2611
2612 void
2613 retreive_geometry() {
2614     int w, h, x, y;
2615     GString *buf = g_string_new("");
2616
2617     gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2618     gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2619
2620     g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2621
2622     if(uzbl.gui.geometry)
2623         g_free(uzbl.gui.geometry);
2624     uzbl.gui.geometry = g_string_free(buf, FALSE);
2625 }
2626
2627 /* set up gtk, gobject, variable defaults and other things that tests and other
2628  * external applications need to do anyhow */
2629 void
2630 initialize(int argc, char *argv[]) {
2631     gtk_init (&argc, &argv);
2632     if (!g_thread_supported ())
2633         g_thread_init (NULL);
2634     uzbl.state.executable_path = g_strdup(argv[0]);
2635     uzbl.state.selected_url = NULL;
2636     uzbl.state.searchtx = NULL;
2637
2638     GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2639     g_option_context_add_main_entries (context, entries, NULL);
2640     g_option_context_add_group (context, gtk_get_option_group (TRUE));
2641     g_option_context_parse (context, &argc, &argv, NULL);
2642     g_option_context_free(context);
2643
2644     if (uzbl.behave.print_version) {
2645         printf("Commit: %s\n", COMMIT);
2646         exit(EXIT_SUCCESS);
2647     }
2648
2649     /* initialize hash table */
2650     uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2651
2652     uzbl.net.soup_session = webkit_get_default_session();
2653     uzbl.state.keycmd = g_strdup("");
2654
2655     if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2656         fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2657     if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2658         fprintf(stderr, "uzbl: error hooking SIGINT\n");
2659     if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2660         fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2661
2662     uzbl.gui.sbar.progress_s = g_strdup("="); //TODO: move these to config.h
2663     uzbl.gui.sbar.progress_u = g_strdup("ยท");
2664     uzbl.gui.sbar.progress_w = 10;
2665
2666     /* HTML mode defaults*/
2667     uzbl.behave.html_buffer = g_string_new("");
2668     uzbl.behave.html_endmarker = g_strdup(".");
2669     uzbl.behave.html_timeout = 60;
2670     uzbl.behave.base_url = g_strdup("http://invalid");
2671
2672     /* default mode indicators */
2673     uzbl.behave.insert_indicator = g_strdup("I");
2674     uzbl.behave.cmd_indicator    = g_strdup("C");
2675
2676     uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2677     uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2678     uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2679     uzbl.info.arch         = ARCH;
2680     uzbl.info.commit       = COMMIT;
2681
2682     commands_hash ();
2683     make_var_to_name_hash();
2684
2685     uzbl.gui.scrolled_win = create_browser();
2686 }
2687
2688 #ifndef UZBL_LIBRARY
2689 /** -- MAIN -- **/
2690 int
2691 main (int argc, char* argv[]) {
2692     initialize(argc, argv);
2693
2694     uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2695
2696     create_mainbar();
2697
2698     /* initial packing */
2699     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2700     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2701
2702     if (uzbl.state.socket_id) {
2703         uzbl.gui.plug = create_plug ();
2704         gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2705         gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2706     } else {
2707         uzbl.gui.main_window = create_window ();
2708         gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2709         gtk_widget_show_all (uzbl.gui.main_window);
2710         uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2711     }
2712
2713     if(!uzbl.state.instance_name)
2714         uzbl.state.instance_name = itos((int)uzbl.xwin);
2715
2716     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2717
2718     if (uzbl.state.verbose) {
2719         printf("Uzbl start location: %s\n", argv[0]);
2720         if (uzbl.state.socket_id)
2721             printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2722         else
2723             printf("window_id %i\n",(int) uzbl.xwin);
2724         printf("pid %i\n", getpid ());
2725         printf("name: %s\n", uzbl.state.instance_name);
2726     }
2727
2728     uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2729     uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2730     uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2731     uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2732     gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2733
2734     if(uzbl.gui.geometry)
2735         cmd_set_geometry();
2736     else
2737         retreive_geometry();
2738
2739     gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2740     if (argc > 1 && !uzbl.state.uri)
2741         uri_override = g_strdup(argv[1]);
2742     gboolean verbose_override = uzbl.state.verbose;
2743
2744     settings_init ();
2745     set_insert_mode(FALSE);
2746
2747     if (!uzbl.behave.show_status)
2748         gtk_widget_hide(uzbl.gui.mainbar);
2749     else
2750         update_title();
2751
2752     /* WebInspector */
2753     set_up_inspector();
2754
2755     if (verbose_override > uzbl.state.verbose)
2756         uzbl.state.verbose = verbose_override;
2757
2758     if (uri_override) {
2759         set_var_value("uri", uri_override);
2760         g_free(uri_override);
2761     } else if (uzbl.state.uri)
2762         cmd_load_uri();
2763
2764     gtk_main ();
2765     clean_up();
2766
2767     return EXIT_SUCCESS;
2768 }
2769 #endif
2770
2771 /* vi: set et ts=4: */