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