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 var_name_to_ptr_t {
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 };
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     const struct var_name_to_ptr_t *n2v_p = var_name_to_ptr;
227     uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
228     while(n2v_p->name) {
229         g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
230         n2v_p++;
231     }
232 }
233
234 /* --- UTILITY FUNCTIONS --- */
235 enum exp_type {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS, EXP_ESCAPE};
236 enum exp_type
237 get_exp_type(const gchar *s) {
238     /* variables */
239     if(*(s+1) == '(')
240         return EXP_EXPR;
241     else if(*(s+1) == '{')
242         return EXP_BRACED_VAR;
243     else if(*(s+1) == '<')
244         return EXP_JS;
245     else if(*(s+1) == '[')
246         return EXP_ESCAPE;
247     else
248         return EXP_SIMPLE_VAR;
249
250     /*@notreached@*/
251 return EXP_ERR;
252 }
253
254 /*
255  * recurse == 1: don't expand '@(command)@'
256  * recurse == 2: don't expand '@<java script>@'
257  */
258 gchar *
259 expand(const char *s, guint recurse) {
260     uzbl_cmdprop *c;
261     enum exp_type etype;
262     char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
263     char *ret = NULL;
264     char *vend = NULL;
265     GError *err = NULL;
266     gchar *cmd_stdout = NULL;
267     gchar *mycmd = NULL;
268     GString *buf = g_string_new("");
269     GString *js_ret = g_string_new("");
270
271     while(*s) {
272         switch(*s) {
273             case '\\':
274                 g_string_append_c(buf, *++s);
275                 s++;
276                 break;
277
278             case '@':
279                 etype = get_exp_type(s);
280                 s++;
281
282                 switch(etype) {
283                     case EXP_SIMPLE_VAR:
284                         vend = strpbrk(s, end_simple_var);
285                         if(!vend) vend = strchr(s, '\0');
286                         break;
287                     case EXP_BRACED_VAR:
288                         s++;
289                         vend = strchr(s, '}');
290                         if(!vend) vend = strchr(s, '\0');
291                         break;
292                     case EXP_EXPR:
293                         s++;
294                         vend = strstr(s, ")@");
295                         if(!vend) vend = strchr(s, '\0');
296                         break;
297                     case EXP_JS:
298                         s++;
299                         vend = strstr(s, ">@");
300                         if(!vend) vend = strchr(s, '\0');
301                         break;
302                     case EXP_ESCAPE:
303                         s++;
304                         vend = strstr(s, "]@");
305                         if(!vend) vend = strchr(s, '\0');
306                         break;
307                 }
308                 assert(vend);
309
310                 ret = g_strndup(s, vend-s);
311
312                 if(etype == EXP_SIMPLE_VAR ||
313                    etype == EXP_BRACED_VAR) {
314                     if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
315                         if(c->type == TYPE_STR && *c->ptr.s != NULL) {
316                             g_string_append(buf, (gchar *)*c->ptr.s);
317                         } else if(c->type == TYPE_INT) {
318                             g_string_append_printf(buf, "%d", *c->ptr.i);
319                         }
320                         else if(c->type == TYPE_FLOAT) {
321                             g_string_append_printf(buf, "%f", *c->ptr.f);
322                         }
323                     }
324
325                     if(etype == EXP_SIMPLE_VAR)
326                         s = vend;
327                     else
328                         s = vend+1;
329                 }
330                 else if(recurse != 1 &&
331                         etype == EXP_EXPR) {
332                     mycmd = expand(ret, 1);
333                     g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
334                     g_free(mycmd);
335
336                     if (err) {
337                         g_printerr("error on running command: %s\n", err->message);
338                         g_error_free (err);
339                     }
340                     else if (*cmd_stdout) {
341                         size_t len = strlen(cmd_stdout);
342
343                         if(len > 0 && cmd_stdout[len-1] == '\n')
344                             cmd_stdout[--len] = '\0'; /* strip trailing newline */
345
346                         g_string_append(buf, cmd_stdout);
347                         g_free(cmd_stdout);
348                     }
349                     s = vend+2;
350                 }
351                 else if(recurse != 2 &&
352                         etype == EXP_JS) {
353                     mycmd = expand(ret, 2);
354                     eval_js(uzbl.gui.web_view, mycmd, js_ret);
355                     g_free(mycmd);
356
357                     if(js_ret->str) {
358                         g_string_append(buf, js_ret->str);
359                         g_string_free(js_ret, TRUE);
360                         js_ret = g_string_new("");
361                     }
362                     s = vend+2;
363                 }
364                 else if(etype == EXP_ESCAPE) {
365                     mycmd = expand(ret, 0);
366                     char *escaped = g_markup_escape_text(mycmd, strlen(mycmd));
367
368                     g_string_append(buf, escaped);
369
370                     g_free(escaped);
371                     g_free(mycmd);
372                     s = vend+2;
373                 }
374
375                 g_free(ret);
376                 ret = NULL;
377                 break;
378
379             default:
380                 g_string_append_c(buf, *s);
381                 s++;
382                 break;
383         }
384     }
385     g_string_free(js_ret, TRUE);
386     return g_string_free(buf, FALSE);
387 }
388
389 char *
390 itos(int val) {
391     char tmp[20];
392
393     snprintf(tmp, sizeof(tmp), "%i", val);
394     return g_strdup(tmp);
395 }
396
397 gchar*
398 strfree(gchar *str) { g_free(str); return NULL; }  // for freeing & setting to null in one go
399
400 gchar*
401 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
402
403 char *
404 str_replace (const char* search, const char* replace, const char* string) {
405     gchar **buf;
406     char *ret;
407
408     buf = g_strsplit (string, search, -1);
409     ret = g_strjoinv (replace, buf);
410     g_strfreev(buf); // somebody said this segfaults
411
412     return ret;
413 }
414
415 GArray*
416 read_file_by_line (const gchar *path) {
417     GIOChannel *chan = NULL;
418     gchar *readbuf = NULL;
419     gsize len;
420     GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
421     int i = 0;
422
423     chan = g_io_channel_new_file(path, "r", NULL);
424
425     if (chan) {
426         while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
427             const gchar* val = g_strdup (readbuf);
428             g_array_append_val (lines, val);
429             g_free (readbuf);
430             i ++;
431         }
432
433         g_io_channel_unref (chan);
434     } else {
435         fprintf(stderr, "File '%s' not be read.\n", path);
436     }
437
438     return lines;
439 }
440
441 gchar*
442 parseenv (char* string) {
443     extern char** environ;
444     gchar* tmpstr = NULL;
445     int i = 0;
446
447
448     while (environ[i] != NULL) {
449         gchar** env = g_strsplit (environ[i], "=", 2);
450         gchar* envname = g_strconcat ("$", env[0], NULL);
451
452         if (g_strrstr (string, envname) != NULL) {
453             tmpstr = g_strdup(string);
454             g_free (string);
455             string = str_replace(envname, env[1], tmpstr);
456             g_free (tmpstr);
457         }
458
459         g_free (envname);
460         g_strfreev (env); // somebody said this breaks uzbl
461         i++;
462     }
463
464     return string;
465 }
466
467 sigfunc*
468 setup_signal(int signr, sigfunc *shandler) {
469     struct sigaction nh, oh;
470
471     nh.sa_handler = shandler;
472     sigemptyset(&nh.sa_mask);
473     nh.sa_flags = 0;
474
475     if(sigaction(signr, &nh, &oh) < 0)
476         return SIG_ERR;
477
478     return NULL;
479 }
480
481 void
482 clean_up(void) {
483     if (uzbl.behave.fifo_dir)
484         unlink (uzbl.comm.fifo_path);
485     if (uzbl.behave.socket_dir)
486         unlink (uzbl.comm.socket_path);
487
488     g_free(uzbl.state.executable_path);
489     g_free(uzbl.state.keycmd);
490     g_hash_table_destroy(uzbl.bindings);
491     g_hash_table_destroy(uzbl.behave.commands);
492 }
493
494 /* used for html_mode_timeout
495  * be sure to extend this function to use
496  * more timers if needed in other places
497 */
498 void
499 set_timeout(int seconds) {
500     struct itimerval t;
501     memset(&t, 0, sizeof t);
502
503     t.it_value.tv_sec =  seconds;
504     t.it_value.tv_usec = 0;
505     setitimer(ITIMER_REAL, &t, NULL);
506 }
507
508 /* --- SIGNAL HANDLER --- */
509
510 void
511 catch_sigterm(int s) {
512     (void) s;
513     clean_up();
514 }
515
516 void
517 catch_sigint(int s) {
518     (void) s;
519     clean_up();
520     exit(EXIT_SUCCESS);
521 }
522
523 void
524 catch_alrm(int s) {
525     (void) s;
526
527     set_var_value("mode", "0");
528     render_html();
529 }
530
531
532 /* --- CALLBACKS --- */
533
534 gboolean
535 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
536     (void) web_view;
537     (void) frame;
538     (void) navigation_action;
539     (void) policy_decision;
540     (void) user_data;
541     const gchar* uri = webkit_network_request_get_uri (request);
542     if (uzbl.state.verbose)
543         printf("New window requested -> %s \n", uri);
544     webkit_web_policy_decision_use(policy_decision);
545     return TRUE;
546 }
547
548 gboolean
549 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type,  WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
550     (void) frame;
551     (void) request;
552     (void) user_data;
553
554     /* If we can display it, let's display it... */
555     if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
556         webkit_web_policy_decision_use (policy_decision);
557         return TRUE;
558     }
559
560     /* ...everything we can't displayed is downloaded */
561     webkit_web_policy_decision_download (policy_decision);
562     return TRUE;
563 }
564
565 /*@null@*/ WebKitWebView*
566 create_web_view_cb (WebKitWebView  *web_view, WebKitWebFrame *frame, gpointer user_data) {
567     (void) web_view;
568     (void) frame;
569     (void) user_data;
570     if (uzbl.state.selected_url != NULL) {
571         if (uzbl.state.verbose)
572             printf("\nNew web view -> %s\n",uzbl.state.selected_url);
573         new_window_load_uri(uzbl.state.selected_url);
574     } else {
575         if (uzbl.state.verbose)
576             printf("New web view -> %s\n","Nothing to open, exiting");
577     }
578     return (NULL);
579 }
580
581 gboolean
582 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
583     (void) web_view;
584     (void) user_data;
585     if (uzbl.behave.download_handler) {
586         const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
587         if (uzbl.state.verbose)
588             printf("Download -> %s\n",uri);
589         /* if urls not escaped, we may have to escape and quote uri before this call */
590         run_handler(uzbl.behave.download_handler, uri);
591     }
592     return (FALSE);
593 }
594
595 /* scroll a bar in a given direction */
596 void
597 scroll (GtkAdjustment* bar, GArray *argv) {
598     gchar *end;
599     gdouble max_value;
600
601     gdouble page_size = gtk_adjustment_get_page_size(bar);
602     gdouble value = gtk_adjustment_get_value(bar);
603     gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
604
605     if (*end == '%')
606         value += page_size * amount * 0.01;
607     else
608         value += amount;
609
610     max_value = gtk_adjustment_get_upper(bar) - page_size;
611
612     if (value > max_value)
613         value = max_value; /* don't scroll past the end of the page */
614
615     gtk_adjustment_set_value (bar, value);
616 }
617
618 void
619 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
620     (void) page; (void) argv; (void) result;
621     gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
622 }
623
624 void
625 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
626     (void) page; (void) argv; (void) result;
627     gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
628                               gtk_adjustment_get_page_size(uzbl.gui.bar_v));
629 }
630
631 void
632 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
633     (void) page; (void) result;
634     scroll(uzbl.gui.bar_v, argv);
635 }
636
637 void
638 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
639     (void) page; (void) result;
640     scroll(uzbl.gui.bar_h, argv);
641 }
642
643 void
644 cmd_set_geometry() {
645     if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
646         if(uzbl.state.verbose)
647             printf("Error in geometry string: %s\n", uzbl.gui.geometry);
648     }
649     /* update geometry var with the actual geometry
650        this is necessary as some WMs don't seem to honour
651        the above setting and we don't want to end up with
652        wrong geometry information
653     */
654     retreive_geometry();
655 }
656
657 void
658 cmd_set_status() {
659     if (!uzbl.behave.show_status) {
660         gtk_widget_hide(uzbl.gui.mainbar);
661     } else {
662         gtk_widget_show(uzbl.gui.mainbar);
663     }
664     update_title();
665 }
666
667 void
668 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
669     (void)page;
670     (void)argv;
671     (void)result;
672
673     webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
674 }
675
676 void
677 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
678     (void)page;
679     (void)argv;
680     (void)result;
681
682     if (uzbl.behave.show_status) {
683         gtk_widget_hide(uzbl.gui.mainbar);
684     } else {
685         gtk_widget_show(uzbl.gui.mainbar);
686     }
687     uzbl.behave.show_status = !uzbl.behave.show_status;
688     update_title();
689 }
690
691 void
692 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
693     (void) page;
694     (void) title;
695     (void) data;
696     //Set selected_url state variable
697     g_free(uzbl.state.selected_url);
698     uzbl.state.selected_url = NULL;
699     if (link) {
700         uzbl.state.selected_url = g_strdup(link);
701     }
702     update_title();
703 }
704
705 void
706 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
707     (void) web_view;
708     (void) param_spec;
709     const gchar *title = webkit_web_view_get_title(web_view);
710     if (uzbl.gui.main_title)
711         g_free (uzbl.gui.main_title);
712     uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
713     update_title();
714 }
715
716 void
717 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
718     (void) page;
719     (void) data;
720     uzbl.gui.sbar.load_progress = progress;
721
722     g_free(uzbl.gui.sbar.progress_bar);
723     uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
724
725     update_title();
726 }
727
728 void
729 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
730     (void) page;
731     (void) frame;
732     (void) data;
733     if (uzbl.behave.load_finish_handler)
734         run_handler(uzbl.behave.load_finish_handler, "");
735 }
736
737 void clear_keycmd() {
738   g_free(uzbl.state.keycmd);
739   uzbl.state.keycmd = g_strdup("");
740 }
741
742 void
743 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
744     (void) page;
745     (void) frame;
746     (void) data;
747     uzbl.gui.sbar.load_progress = 0;
748     clear_keycmd(); // don't need old commands to remain on new page?
749     if (uzbl.behave.load_start_handler)
750         run_handler(uzbl.behave.load_start_handler, "");
751 }
752
753 void
754 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
755     (void) page;
756     (void) data;
757     g_free (uzbl.state.uri);
758     GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
759     uzbl.state.uri = g_string_free (newuri, FALSE);
760     if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
761         set_insert_mode(uzbl.behave.always_insert_mode);
762         update_title();
763     }
764     if (uzbl.behave.load_commit_handler)
765         run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
766 }
767
768 void
769 destroy_cb (GtkWidget* widget, gpointer data) {
770     (void) widget;
771     (void) data;
772     gtk_main_quit ();
773 }
774
775 void
776 log_history_cb () {
777    if (uzbl.behave.history_handler) {
778        time_t rawtime;
779        struct tm * timeinfo;
780        char date [80];
781        time ( &rawtime );
782        timeinfo = localtime ( &rawtime );
783        strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
784        run_handler(uzbl.behave.history_handler, date);
785    }
786 }
787
788
789 /* VIEW funcs (little webkit wrappers) */
790 #define VIEWFUNC(name) void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
791 VIEWFUNC(reload)
792 VIEWFUNC(reload_bypass_cache)
793 VIEWFUNC(stop_loading)
794 VIEWFUNC(zoom_in)
795 VIEWFUNC(zoom_out)
796 VIEWFUNC(go_back)
797 VIEWFUNC(go_forward)
798 #undef VIEWFUNC
799
800 /* -- command to callback/function map for things we cannot attach to any signals */
801 struct {const char *key; CommandInfo value;} cmdlist[] =
802 {   /* key                   function      no_split      */
803     { "back",               {view_go_back, 0}              },
804     { "forward",            {view_go_forward, 0}           },
805     { "scroll_vert",        {scroll_vert, 0}               },
806     { "scroll_horz",        {scroll_horz, 0}               },
807     { "scroll_begin",       {scroll_begin, 0}              },
808     { "scroll_end",         {scroll_end, 0}                },
809     { "reload",             {view_reload, 0},              },
810     { "reload_ign_cache",   {view_reload_bypass_cache, 0}  },
811     { "stop",               {view_stop_loading, 0},        },
812     { "zoom_in",            {view_zoom_in, 0},             }, //Can crash (when max zoom reached?).
813     { "zoom_out",           {view_zoom_out, 0},            },
814     { "toggle_zoom_type",   {toggle_zoom_type, 0},         },
815     { "uri",                {load_uri, TRUE}               },
816     { "js",                 {run_js, TRUE}                 },
817     { "script",             {run_external_js, 0}           },
818     { "toggle_status",      {toggle_status_cb, 0}          },
819     { "spawn",              {spawn, 0}                     },
820     { "sync_spawn",         {spawn_sync, 0}                }, // needed for cookie handler
821     { "sh",                 {spawn_sh, 0}                  },
822     { "sync_sh",            {spawn_sh_sync, 0}             }, // needed for cookie handler
823     { "talk_to_socket",     {talk_to_socket, 0}            },
824     { "exit",               {close_uzbl, 0}                },
825     { "search",             {search_forward_text, TRUE}    },
826     { "search_reverse",     {search_reverse_text, TRUE}    },
827     { "dehilight",          {dehilight, 0}                 },
828     { "toggle_insert_mode", {toggle_insert_mode, 0}        },
829     { "set",                {set_var, TRUE}                },
830   //{ "get",                {get_var, TRUE}                },
831     { "bind",               {act_bind, TRUE}               },
832     { "dump_config",        {act_dump_config, 0}           },
833     { "keycmd",             {keycmd, TRUE}                 },
834     { "keycmd_nl",          {keycmd_nl, TRUE}              },
835     { "keycmd_bs",          {keycmd_bs, 0}                 },
836     { "chain",              {chain, 0}                     },
837     { "print",              {print, TRUE}                  }
838 };
839
840 void
841 commands_hash(void)
842 {
843     unsigned int i;
844     uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
845
846     for (i = 0; i < LENGTH(cmdlist); i++)
847         g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
848 }
849
850 /* -- CORE FUNCTIONS -- */
851
852 void
853 free_action(gpointer act) {
854     Action *action = (Action*)act;
855     g_free(action->name);
856     if (action->param)
857         g_free(action->param);
858     g_free(action);
859 }
860
861 Action*
862 new_action(const gchar *name, const gchar *param) {
863     Action *action = g_new(Action, 1);
864
865     action->name = g_strdup(name);
866     if (param)
867         action->param = g_strdup(param);
868     else
869         action->param = NULL;
870
871     return action;
872 }
873
874 bool
875 file_exists (const char * filename) {
876     return (access(filename, F_OK) == 0);
877 }
878
879 void
880 set_var(WebKitWebView *page, GArray *argv, GString *result) {
881     (void) page; (void) result;
882     gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
883     if (split[0] != NULL) {
884         gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
885         set_var_value(g_strstrip(split[0]), value);
886         g_free(value);
887     }
888     g_strfreev(split);
889 }
890
891 void
892 print(WebKitWebView *page, GArray *argv, GString *result) {
893     (void) page; (void) result;
894     gchar* buf;
895
896     buf = expand(argv_idx(argv, 0), 0);
897     g_string_assign(result, buf);
898     g_free(buf);
899 }
900
901 void
902 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
903     (void) page; (void) result;
904     gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
905     gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
906     add_binding(g_strstrip(split[0]), value);
907     g_free(value);
908     g_strfreev(split);
909 }
910
911
912 void
913 act_dump_config() {
914     dump_config();
915 }
916
917 void
918 set_keycmd() {
919     run_keycmd(FALSE);
920     update_title();
921 }
922
923 void
924 set_mode_indicator() {
925     uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
926         uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
927 }
928
929 void
930 update_indicator() {
931   set_mode_indicator();
932   update_title();
933 }
934
935 void
936 set_insert_mode(gboolean mode) {
937     uzbl.behave.insert_mode = mode;
938     set_mode_indicator();
939 }
940
941 void
942 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
943     (void) page; (void) result;
944
945     if (argv_idx(argv, 0)) {
946         if (strcmp (argv_idx(argv, 0), "0") == 0) {
947             set_insert_mode(FALSE);
948         } else {
949             set_insert_mode(TRUE);
950         }
951     } else {
952         set_insert_mode( !uzbl.behave.insert_mode );
953     }
954
955     update_title();
956 }
957
958 void
959 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
960     (void) result;
961
962     if (argv_idx(argv, 0)) {
963         GString* newuri = g_string_new (argv_idx(argv, 0));
964         if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
965             run_js(web_view, argv, NULL);
966             return;
967         }
968         if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
969             g_string_prepend (newuri, "http://");
970         /* if we do handle cookies, ask our handler for them */
971         webkit_web_view_load_uri (web_view, newuri->str);
972         g_string_free (newuri, TRUE);
973     }
974 }
975
976 /* Javascript*/
977
978 JSValueRef
979 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
980                 size_t argumentCount, const JSValueRef arguments[],
981                 JSValueRef* exception) {
982     (void) function;
983     (void) thisObject;
984     (void) exception;
985
986     JSStringRef js_result_string;
987     GString *result = g_string_new("");
988
989     if (argumentCount >= 1) {
990         JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
991         size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
992         char ctl_line[arg_size];
993         JSStringGetUTF8CString(arg, ctl_line, arg_size);
994
995         parse_cmd_line(ctl_line, result);
996
997         JSStringRelease(arg);
998     }
999     js_result_string = JSStringCreateWithUTF8CString(result->str);
1000
1001     g_string_free(result, TRUE);
1002
1003     return JSValueMakeString(ctx, js_result_string);
1004 }
1005
1006 JSStaticFunction js_static_functions[] = {
1007     {"run", js_run_command, kJSPropertyAttributeNone},
1008 };
1009
1010 void
1011 js_init() {
1012     /* This function creates the class and its definition, only once */
1013     if (!uzbl.js.initialized) {
1014         /* it would be pretty cool to make this dynamic */
1015         uzbl.js.classdef = kJSClassDefinitionEmpty;
1016         uzbl.js.classdef.staticFunctions = js_static_functions;
1017
1018         uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
1019     }
1020 }
1021
1022
1023 void
1024 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
1025     WebKitWebFrame *frame;
1026     JSGlobalContextRef context;
1027     JSObjectRef globalobject;
1028     JSStringRef var_name;
1029
1030     JSStringRef js_script;
1031     JSValueRef js_result;
1032     JSStringRef js_result_string;
1033     size_t js_result_size;
1034
1035     js_init();
1036
1037     frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
1038     context = webkit_web_frame_get_global_context(frame);
1039     globalobject = JSContextGetGlobalObject(context);
1040
1041     /* uzbl javascript namespace */
1042     var_name = JSStringCreateWithUTF8CString("Uzbl");
1043     JSObjectSetProperty(context, globalobject, var_name,
1044                         JSObjectMake(context, uzbl.js.classref, NULL),
1045                         kJSClassAttributeNone, NULL);
1046
1047     /* evaluate the script and get return value*/
1048     js_script = JSStringCreateWithUTF8CString(script);
1049     js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1050     if (js_result && !JSValueIsUndefined(context, js_result)) {
1051         js_result_string = JSValueToStringCopy(context, js_result, NULL);
1052         js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1053
1054         if (js_result_size) {
1055             char js_result_utf8[js_result_size];
1056             JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1057             g_string_assign(result, js_result_utf8);
1058         }
1059
1060         JSStringRelease(js_result_string);
1061     }
1062
1063     /* cleanup */
1064     JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1065
1066     JSStringRelease(var_name);
1067     JSStringRelease(js_script);
1068 }
1069
1070 void
1071 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1072     if (argv_idx(argv, 0))
1073         eval_js(web_view, argv_idx(argv, 0), result);
1074 }
1075
1076 void
1077 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1078     (void) result;
1079     if (argv_idx(argv, 0)) {
1080         GArray* lines = read_file_by_line (argv_idx (argv, 0));
1081         gchar*  js = NULL;
1082         int i = 0;
1083         gchar* line;
1084
1085         while ((line = g_array_index(lines, gchar*, i))) {
1086             if (js == NULL) {
1087                 js = g_strdup (line);
1088             } else {
1089                 gchar* newjs = g_strconcat (js, line, NULL);
1090                 js = newjs;
1091             }
1092             i ++;
1093             g_free (line);
1094         }
1095
1096         if (uzbl.state.verbose)
1097             printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1098
1099         if (argv_idx (argv, 1)) {
1100             gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1101             g_free (js);
1102             js = newjs;
1103         }
1104         eval_js (web_view, js, result);
1105         g_free (js);
1106         g_array_free (lines, TRUE);
1107     }
1108 }
1109
1110 void
1111 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1112     if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1113         if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1114             webkit_web_view_unmark_text_matches (page);
1115             webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1116             uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1117         }
1118     }
1119
1120     if (uzbl.state.searchtx) {
1121         if (uzbl.state.verbose)
1122             printf ("Searching: %s\n", uzbl.state.searchtx);
1123         webkit_web_view_set_highlight_text_matches (page, TRUE);
1124         webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1125     }
1126 }
1127
1128 void
1129 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1130     (void) result;
1131     search_text(page, argv, TRUE);
1132 }
1133
1134 void
1135 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1136     (void) result;
1137     search_text(page, argv, FALSE);
1138 }
1139
1140 void
1141 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1142     (void) argv; (void) result;
1143     webkit_web_view_set_highlight_text_matches (page, FALSE);
1144 }
1145
1146
1147 void
1148 new_window_load_uri (const gchar * uri) {
1149     if (uzbl.behave.new_window) {
1150         GString *s = g_string_new ("");
1151         g_string_printf(s, "'%s'", uri);
1152         run_handler(uzbl.behave.new_window, s->str);
1153         return;
1154     }
1155     GString* to_execute = g_string_new ("");
1156     g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1157     int i;
1158     for (i = 0; entries[i].long_name != NULL; i++) {
1159         if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1160             gchar** str = (gchar**)entries[i].arg_data;
1161             if (*str!=NULL) {
1162                 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1163             }
1164         }
1165     }
1166     if (uzbl.state.verbose)
1167         printf("\n%s\n", to_execute->str);
1168     g_spawn_command_line_async (to_execute->str, NULL);
1169     g_string_free (to_execute, TRUE);
1170 }
1171
1172 void
1173 chain (WebKitWebView *page, GArray *argv, GString *result) {
1174     (void) page; (void) result;
1175     gchar *a = NULL;
1176     gchar **parts = NULL;
1177     guint i = 0;
1178     while ((a = argv_idx(argv, i++))) {
1179         parts = g_strsplit (a, " ", 2);
1180         if (parts[0])
1181           parse_command(parts[0], parts[1], result);
1182         g_strfreev (parts);
1183     }
1184 }
1185
1186 void
1187 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1188     (void)page;
1189     (void)argv;
1190     (void)result;
1191     uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1192     run_keycmd(FALSE);
1193     update_title();
1194 }
1195
1196 void
1197 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1198     (void)page;
1199     (void)argv;
1200     (void)result;
1201     uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1202     run_keycmd(TRUE);
1203     update_title();
1204 }
1205
1206 void
1207 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1208     gchar *prev;
1209     (void)page;
1210     (void)argv;
1211     (void)result;
1212     int len = strlen(uzbl.state.keycmd);
1213     prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1214     if (prev)
1215       uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1216     update_title();
1217 }
1218
1219 void
1220 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1221     (void)page;
1222     (void)argv;
1223     (void)result;
1224     gtk_main_quit ();
1225 }
1226
1227 /* --Statusbar functions-- */
1228 char*
1229 build_progressbar_ascii(int percent) {
1230    int width=uzbl.gui.sbar.progress_w;
1231    int i;
1232    double l;
1233    GString *bar = g_string_new("");
1234
1235    l = (double)percent*((double)width/100.);
1236    l = (int)(l+.5)>=(int)l ? l+.5 : l;
1237
1238    for(i=0; i<(int)l; i++)
1239        g_string_append(bar, uzbl.gui.sbar.progress_s);
1240
1241    for(; i<width; i++)
1242        g_string_append(bar, uzbl.gui.sbar.progress_u);
1243
1244    return g_string_free(bar, FALSE);
1245 }
1246 /* --End Statusbar functions-- */
1247
1248 void
1249 sharg_append(GArray *a, const gchar *str) {
1250     const gchar *s = (str ? str : "");
1251     g_array_append_val(a, s);
1252 }
1253
1254 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1255 gboolean
1256 run_command (const gchar *command, const guint npre, const gchar **args,
1257              const gboolean sync, char **output_stdout) {
1258    //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1259     GError *err = NULL;
1260
1261     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1262     gchar *pid = itos(getpid());
1263     gchar *xwin = itos(uzbl.xwin);
1264     guint i;
1265     sharg_append(a, command);
1266     for (i = 0; i < npre; i++) /* add n args before the default vars */
1267         sharg_append(a, args[i]);
1268     sharg_append(a, uzbl.state.config_file);
1269     sharg_append(a, pid);
1270     sharg_append(a, xwin);
1271     sharg_append(a, uzbl.comm.fifo_path);
1272     sharg_append(a, uzbl.comm.socket_path);
1273     sharg_append(a, uzbl.state.uri);
1274     sharg_append(a, uzbl.gui.main_title);
1275
1276     for (i = npre; i < g_strv_length((gchar**)args); i++)
1277         sharg_append(a, args[i]);
1278
1279     gboolean result;
1280     if (sync) {
1281         if (*output_stdout) *output_stdout = strfree(*output_stdout);
1282
1283         result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1284                               NULL, NULL, output_stdout, NULL, NULL, &err);
1285     } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1286                                   NULL, NULL, NULL, &err);
1287
1288     if (uzbl.state.verbose) {
1289         GString *s = g_string_new("spawned:");
1290         for (i = 0; i < (a->len); i++) {
1291             gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1292             g_string_append_printf(s, " %s", qarg);
1293             g_free (qarg);
1294         }
1295         g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1296         printf("%s\n", s->str);
1297         g_string_free(s, TRUE);
1298         if(output_stdout) {
1299             printf("Stdout: %s\n", *output_stdout);
1300         }
1301     }
1302     if (err) {
1303         g_printerr("error on run_command: %s\n", err->message);
1304         g_error_free (err);
1305     }
1306     g_free (pid);
1307     g_free (xwin);
1308     g_array_free (a, TRUE);
1309     return result;
1310 }
1311
1312 /*@null@*/ gchar**
1313 split_quoted(const gchar* src, const gboolean unquote) {
1314     /* split on unquoted space, return array of strings;
1315        remove a layer of quotes and backslashes if unquote */
1316     if (!src) return NULL;
1317
1318     gboolean dq = FALSE;
1319     gboolean sq = FALSE;
1320     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1321     GString *s = g_string_new ("");
1322     const gchar *p;
1323     gchar **ret;
1324     gchar *dup;
1325     for (p = src; *p != '\0'; p++) {
1326         if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1327         else if (*p == '\\') { g_string_append_c(s, *p++);
1328                                g_string_append_c(s, *p); }
1329         else if ((*p == '"') && unquote && !sq) dq = !dq;
1330         else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1331                                      dq = !dq; }
1332         else if ((*p == '\'') && unquote && !dq) sq = !sq;
1333         else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1334                                       sq = ! sq; }
1335         else if ((*p == ' ') && !dq && !sq) {
1336             dup = g_strdup(s->str);
1337             g_array_append_val(a, dup);
1338             g_string_truncate(s, 0);
1339         } else g_string_append_c(s, *p);
1340     }
1341     dup = g_strdup(s->str);
1342     g_array_append_val(a, dup);
1343     ret = (gchar**)a->data;
1344     g_array_free (a, FALSE);
1345     g_string_free (s, TRUE);
1346     return ret;
1347 }
1348
1349 void
1350 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1351     (void)web_view; (void)result;
1352     //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1353     if (argv_idx(argv, 0))
1354         run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1355 }
1356
1357 void
1358 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1359     (void)web_view; (void)result;
1360
1361     if (argv_idx(argv, 0))
1362         run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1363                     TRUE, &uzbl.comm.sync_stdout);
1364 }
1365
1366 void
1367 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1368     (void)web_view; (void)result;
1369     if (!uzbl.behave.shell_cmd) {
1370         g_printerr ("spawn_sh: shell_cmd is not set!\n");
1371         return;
1372     }
1373
1374     guint i;
1375     gchar *spacer = g_strdup("");
1376     g_array_insert_val(argv, 1, spacer);
1377     gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1378
1379     for (i = 1; i < g_strv_length(cmd); i++)
1380         g_array_prepend_val(argv, cmd[i]);
1381
1382     if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1383     g_free (spacer);
1384     g_strfreev (cmd);
1385 }
1386
1387 void
1388 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1389     (void)web_view; (void)result;
1390     if (!uzbl.behave.shell_cmd) {
1391         g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1392         return;
1393     }
1394
1395     guint i;
1396     gchar *spacer = g_strdup("");
1397     g_array_insert_val(argv, 1, spacer);
1398     gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1399
1400     for (i = 1; i < g_strv_length(cmd); i++)
1401         g_array_prepend_val(argv, cmd[i]);
1402
1403     if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1404                          TRUE, &uzbl.comm.sync_stdout);
1405     g_free (spacer);
1406     g_strfreev (cmd);
1407 }
1408
1409 void
1410 talk_to_socket(WebKitWebView *web_view, GArray *argv, GString *result) {
1411     (void)web_view; (void)result;
1412
1413     int fd, len;
1414     struct sockaddr_un sa;
1415     char* sockpath;
1416     ssize_t ret;
1417     struct pollfd pfd;
1418     struct iovec* iov;
1419     guint i;
1420
1421     if(uzbl.comm.sync_stdout) uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
1422
1423     /* This function could be optimised by storing a hash table of socket paths
1424        and associated connected file descriptors rather than closing and
1425        re-opening for every call. Also we could launch a script if socket connect
1426        fails. */
1427
1428     /* First element argv[0] is path to socket. Following elements are tokens to
1429        write to the socket. We write them as a single packet with each token
1430        separated by an ASCII nul (\0). */
1431     if(argv->len < 2) {
1432         g_printerr("talk_to_socket called with only %d args (need at least two).\n",
1433             (int)argv->len);
1434         return;
1435     }
1436
1437     /* copy socket path, null terminate result */
1438     sockpath = g_array_index(argv, char*, 0);
1439     g_strlcpy(sa.sun_path, sockpath, sizeof(sa.sun_path));
1440     sa.sun_family = AF_UNIX;
1441
1442     /* create socket file descriptor and connect it to path */
1443     fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
1444     if(fd == -1) {
1445         g_printerr("talk_to_socket: creating socket failed (%s)\n", strerror(errno));
1446         return;
1447     }
1448     if(connect(fd, (struct sockaddr*)&sa, sizeof(sa))) {
1449         g_printerr("talk_to_socket: connect failed (%s)\n", strerror(errno));
1450         close(fd);
1451         return;
1452     }
1453
1454     /* build request vector */
1455     iov = g_malloc(sizeof(struct iovec) * (argv->len - 1));
1456     if(!iov) {
1457         g_printerr("talk_to_socket: unable to allocated memory for token vector\n");
1458         close(fd);
1459         return;
1460     }
1461     for(i = 1; i < argv->len; ++i) {
1462         iov[i - 1].iov_base = g_array_index(argv, char*, i);
1463         iov[i - 1].iov_len = strlen(iov[i - 1].iov_base) + 1; /* string plus \0 */
1464     }
1465
1466     /* write request */
1467     ret = writev(fd, iov, argv->len - 1);
1468     g_free(iov);
1469     if(ret == -1) {
1470         g_printerr("talk_to_socket: write failed (%s)\n", strerror(errno));
1471         close(fd);
1472         return;
1473     }
1474
1475     /* wait for a response, with a 500ms timeout */
1476     pfd.fd = fd;
1477     pfd.events = POLLIN;
1478     while(1) {
1479         ret = poll(&pfd, 1, 500);
1480         if(ret == 1) break;
1481         if(ret == 0) errno = ETIMEDOUT;
1482         if(errno == EINTR) continue;
1483         g_printerr("talk_to_socket: poll failed while waiting for input (%s)\n",
1484             strerror(errno));
1485         close(fd);
1486         return;
1487     }
1488
1489     /* get length of response */
1490     if(ioctl(fd, FIONREAD, &len) == -1) {
1491         g_printerr("talk_to_socket: cannot find daemon response length, "
1492             "ioctl failed (%s)\n", strerror(errno));
1493         close(fd);
1494         return;
1495     }
1496
1497     /* if there is a response, read it */
1498     if(len) {
1499         uzbl.comm.sync_stdout = g_malloc(len + 1);
1500         if(!uzbl.comm.sync_stdout) {
1501             g_printerr("talk_to_socket: failed to allocate %d bytes\n", len);
1502             close(fd);
1503             return;
1504         }
1505         uzbl.comm.sync_stdout[len] = 0; /* ensure result is null terminated */
1506
1507         ret = read(fd, uzbl.comm.sync_stdout, len);
1508         if(ret == -1) {
1509             g_printerr("talk_to_socket: failed to read from socket (%s)\n",
1510                 strerror(errno));
1511             close(fd);
1512             return;
1513         }
1514     }
1515
1516     /* clean up */
1517     close(fd);
1518     return;
1519 }
1520
1521 void
1522 parse_command(const char *cmd, const char *param, GString *result) {
1523     CommandInfo *c;
1524
1525     if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1526             guint i;
1527             gchar **par = split_quoted(param, TRUE);
1528             GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1529
1530             if (c->no_split) { /* don't split */
1531                 sharg_append(a, param);
1532             } else if (par) {
1533                 for (i = 0; i < g_strv_length(par); i++)
1534                     sharg_append(a, par[i]);
1535             }
1536
1537             if (result == NULL) {
1538                 GString *result_print = g_string_new("");
1539
1540                 c->function(uzbl.gui.web_view, a, result_print);
1541                 if (result_print->len)
1542                     printf("%*s\n", (int)result_print->len, result_print->str);
1543
1544                 g_string_free(result_print, TRUE);
1545             } else {
1546                 c->function(uzbl.gui.web_view, a, result);
1547             }
1548             g_strfreev (par);
1549             g_array_free (a, TRUE);
1550
1551     } else
1552         g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1553 }
1554
1555 void
1556 set_proxy_url() {
1557     SoupURI *suri;
1558
1559     if(uzbl.net.proxy_url == NULL || *uzbl.net.proxy_url == ' ') {
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(const 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 /*@null@*/ 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 /*@null@*/ 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 /*@null@*/ 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 /*@null@*/ 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 /*@null@*/ gchar*
2531 find_xdg_file (int xdg_type, const 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         g_free(temporary_file);
2561         return NULL;
2562     }
2563 }
2564 void
2565 settings_init () {
2566     State *s = &uzbl.state;
2567     Network *n = &uzbl.net;
2568     int i;
2569     for (i = 0; default_config[i].command != NULL; i++) {
2570         parse_cmd_line(default_config[i].command, NULL);
2571     }
2572
2573     if (g_strcmp0(s->config_file, "-") == 0) {
2574         s->config_file = NULL;
2575         create_stdin();
2576     }
2577
2578     else if (!s->config_file) {
2579         s->config_file = find_xdg_file (0, "/uzbl/config");
2580     }
2581
2582     if (s->config_file) {
2583         GArray* lines = read_file_by_line (s->config_file);
2584         int i = 0;
2585         gchar* line;
2586
2587         while ((line = g_array_index(lines, gchar*, i))) {
2588             parse_cmd_line (line, NULL);
2589             i ++;
2590             g_free (line);
2591         }
2592         g_array_free (lines, TRUE);
2593     } else {
2594         if (uzbl.state.verbose)
2595             printf ("No configuration file loaded.\n");
2596     }
2597
2598     g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2599 }
2600
2601 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2602     (void) session;
2603     (void) user_data;
2604     if (!uzbl.behave.cookie_handler)
2605          return;
2606
2607     soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2608     GString *s = g_string_new ("");
2609     SoupURI * soup_uri = soup_message_get_uri(msg);
2610     g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2611     run_handler(uzbl.behave.cookie_handler, s->str);
2612
2613     if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2614         char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2615         if ( p != NULL ) *p = '\0';
2616         soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2617     }
2618     if (uzbl.comm.sync_stdout)
2619         uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2620
2621     g_string_free(s, TRUE);
2622 }
2623
2624 void
2625 save_cookies (SoupMessage *msg, gpointer user_data){
2626     (void) user_data;
2627     GSList *ck;
2628     char *cookie;
2629     for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2630         cookie = soup_cookie_to_set_cookie_header(ck->data);
2631         SoupURI * soup_uri = soup_message_get_uri(msg);
2632         GString *s = g_string_new ("");
2633         g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2634         run_handler(uzbl.behave.cookie_handler, s->str);
2635         g_free (cookie);
2636         g_string_free(s, TRUE);
2637     }
2638     g_slist_free(ck);
2639 }
2640
2641 /* --- WEBINSPECTOR --- */
2642 void
2643 hide_window_cb(GtkWidget *widget, gpointer data) {
2644     (void) data;
2645
2646     gtk_widget_hide(widget);
2647 }
2648
2649 WebKitWebView*
2650 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2651     (void) data;
2652     (void) page;
2653     (void) web_inspector;
2654     GtkWidget* scrolled_window;
2655     GtkWidget* new_web_view;
2656     GUI *g = &uzbl.gui;
2657
2658     g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2659     g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2660             G_CALLBACK(hide_window_cb), NULL);
2661
2662     gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2663     gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2664     gtk_widget_show(g->inspector_window);
2665
2666     scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2667     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2668             GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2669     gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2670     gtk_widget_show(scrolled_window);
2671
2672     new_web_view = webkit_web_view_new();
2673     gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2674
2675     return WEBKIT_WEB_VIEW(new_web_view);
2676 }
2677
2678 gboolean
2679 inspector_show_window_cb (WebKitWebInspector* inspector){
2680     (void) inspector;
2681     gtk_widget_show(uzbl.gui.inspector_window);
2682     return TRUE;
2683 }
2684
2685 /* TODO: Add variables and code to make use of these functions */
2686 gboolean
2687 inspector_close_window_cb (WebKitWebInspector* inspector){
2688     (void) inspector;
2689     return TRUE;
2690 }
2691
2692 gboolean
2693 inspector_attach_window_cb (WebKitWebInspector* inspector){
2694     (void) inspector;
2695     return FALSE;
2696 }
2697
2698 gboolean
2699 inspector_detach_window_cb (WebKitWebInspector* inspector){
2700     (void) inspector;
2701     return FALSE;
2702 }
2703
2704 gboolean
2705 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2706     (void) inspector;
2707     return FALSE;
2708 }
2709
2710 gboolean
2711 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2712     (void) inspector;
2713     return FALSE;
2714 }
2715
2716 void
2717 set_up_inspector() {
2718     GUI *g = &uzbl.gui;
2719     WebKitWebSettings *settings = view_settings();
2720     g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2721
2722     uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2723     g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2724     g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2725     g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2726     g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2727     g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2728     g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2729
2730     g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2731 }
2732
2733 void
2734 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2735     (void) ud;
2736     uzbl_cmdprop *c = v;
2737
2738     if(!c->dump)
2739         return;
2740
2741     if(c->type == TYPE_STR)
2742         printf("set %s = %s\n", (char *)k, *c->ptr.s ? *c->ptr.s : " ");
2743     else if(c->type == TYPE_INT)
2744         printf("set %s = %d\n", (char *)k, *c->ptr.i);
2745     else if(c->type == TYPE_FLOAT)
2746         printf("set %s = %f\n", (char *)k, *c->ptr.f);
2747 }
2748
2749 void
2750 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2751     (void) ud;
2752     Action *a = v;
2753
2754     printf("bind %s = %s %s\n", (char *)k ,
2755             (char *)a->name, a->param?(char *)a->param:"");
2756 }
2757
2758 void
2759 dump_config() {
2760     g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2761     g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2762 }
2763
2764 void
2765 retreive_geometry() {
2766     int w, h, x, y;
2767     GString *buf = g_string_new("");
2768
2769     gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2770     gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2771
2772     g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2773
2774     if(uzbl.gui.geometry)
2775         g_free(uzbl.gui.geometry);
2776     uzbl.gui.geometry = g_string_free(buf, FALSE);
2777 }
2778
2779 /* set up gtk, gobject, variable defaults and other things that tests and other
2780  * external applications need to do anyhow */
2781 void
2782 initialize(int argc, char *argv[]) {
2783     if (!g_thread_supported ())
2784         g_thread_init (NULL);
2785     uzbl.state.executable_path = g_strdup(argv[0]);
2786     uzbl.state.selected_url = NULL;
2787     uzbl.state.searchtx = NULL;
2788
2789     GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2790     g_option_context_add_main_entries (context, entries, NULL);
2791     g_option_context_add_group (context, gtk_get_option_group (TRUE));
2792     g_option_context_parse (context, &argc, &argv, NULL);
2793     g_option_context_free(context);
2794
2795     if (uzbl.behave.print_version) {
2796         printf("Commit: %s\n", COMMIT);
2797         exit(EXIT_SUCCESS);
2798     }
2799
2800     /* initialize hash table */
2801     uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2802
2803     uzbl.net.soup_session = webkit_get_default_session();
2804     uzbl.state.keycmd = g_strdup("");
2805
2806     if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2807         fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2808     if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2809         fprintf(stderr, "uzbl: error hooking SIGINT\n");
2810     if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2811         fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2812
2813     uzbl.gui.sbar.progress_s = g_strdup("="); //TODO: move these to config.h
2814     uzbl.gui.sbar.progress_u = g_strdup("ยท");
2815     uzbl.gui.sbar.progress_w = 10;
2816
2817     /* HTML mode defaults*/
2818     uzbl.behave.html_buffer = g_string_new("");
2819     uzbl.behave.html_endmarker = g_strdup(".");
2820     uzbl.behave.html_timeout = 60;
2821     uzbl.behave.base_url = g_strdup("http://invalid");
2822
2823     /* default mode indicators */
2824     uzbl.behave.insert_indicator = g_strdup("I");
2825     uzbl.behave.cmd_indicator    = g_strdup("C");
2826
2827     uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2828     uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2829     uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2830     uzbl.info.arch         = ARCH;
2831     uzbl.info.commit       = COMMIT;
2832
2833     commands_hash ();
2834     make_var_to_name_hash();
2835
2836     create_browser();
2837 }
2838
2839 #ifndef UZBL_LIBRARY
2840 /** -- MAIN -- **/
2841 int
2842 main (int argc, char* argv[]) {
2843     initialize(argc, argv);
2844
2845     gtk_init (&argc, &argv);
2846
2847     uzbl.gui.scrolled_win = gtk_scrolled_window_new (NULL, NULL);
2848     //main_window_ref = g_object_ref(scrolled_window);
2849     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (uzbl.gui.scrolled_win),
2850         GTK_POLICY_NEVER, GTK_POLICY_NEVER); //todo: some sort of display of position/total length. like what emacs does
2851
2852     gtk_container_add (GTK_CONTAINER (uzbl.gui.scrolled_win),
2853         GTK_WIDGET (uzbl.gui.web_view));
2854
2855     uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2856
2857     create_mainbar();
2858
2859     /* initial packing */
2860     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2861     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2862
2863     if (uzbl.state.socket_id) {
2864         uzbl.gui.plug = create_plug ();
2865         gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2866         gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2867     } else {
2868         uzbl.gui.main_window = create_window ();
2869         gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2870         gtk_widget_show_all (uzbl.gui.main_window);
2871         uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2872     }
2873
2874     if(!uzbl.state.instance_name)
2875         uzbl.state.instance_name = itos((int)uzbl.xwin);
2876
2877     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2878
2879     if (uzbl.state.verbose) {
2880         printf("Uzbl start location: %s\n", argv[0]);
2881         if (uzbl.state.socket_id)
2882             printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2883         else
2884             printf("window_id %i\n",(int) uzbl.xwin);
2885         printf("pid %i\n", getpid ());
2886         printf("name: %s\n", uzbl.state.instance_name);
2887     }
2888
2889     uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2890     uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2891     uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2892     uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2893     gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2894
2895     if(uzbl.gui.geometry)
2896         cmd_set_geometry();
2897     else
2898         retreive_geometry();
2899
2900     gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2901     if (argc > 1 && !uzbl.state.uri)
2902         uri_override = g_strdup(argv[1]);
2903     gboolean verbose_override = uzbl.state.verbose;
2904
2905     settings_init ();
2906     set_insert_mode(FALSE);
2907
2908     if (!uzbl.behave.show_status)
2909         gtk_widget_hide(uzbl.gui.mainbar);
2910     else
2911         update_title();
2912
2913     /* WebInspector */
2914     set_up_inspector();
2915
2916     if (verbose_override > uzbl.state.verbose)
2917         uzbl.state.verbose = verbose_override;
2918
2919     if (uri_override) {
2920         set_var_value("uri", uri_override);
2921         g_free(uri_override);
2922     } else if (uzbl.state.uri)
2923         cmd_load_uri();
2924
2925     gtk_main ();
2926     clean_up();
2927
2928     return EXIT_SUCCESS;
2929 }
2930 #endif
2931
2932 /* vi: set et ts=4: */