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