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