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