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