Merge commit 'pawelz/experimental' 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_Up        ||
2240         event->keyval == GDK_Down      ||
2241         event->keyval == GDK_Left      ||
2242         event->keyval == GDK_Right     ||
2243         event->keyval == GDK_Shift_L   ||
2244         event->keyval == GDK_Shift_R)
2245         return FALSE;
2246
2247     /* turn off insert mode (if always_insert_mode is not used) */
2248     if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2249         set_insert_mode(uzbl.behave.always_insert_mode);
2250         update_title();
2251         return TRUE;
2252     }
2253
2254     if (uzbl.behave.insert_mode &&
2255         ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
2256           (!uzbl.behave.modmask)
2257         )
2258        )
2259         return FALSE;
2260
2261     if (event->keyval == GDK_Escape) {
2262         clear_keycmd();
2263         update_title();
2264         dehilight(uzbl.gui.web_view, NULL, NULL);
2265         return TRUE;
2266     }
2267
2268     //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2269     if (event->keyval == GDK_Insert) {
2270         gchar * str;
2271         if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2272             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2273         } else {
2274             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2275         }
2276         if (str) {
2277             GString* keycmd = g_string_new(uzbl.state.keycmd);
2278             g_string_append (keycmd, str);
2279             uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2280             update_title ();
2281             g_free (str);
2282         }
2283         return TRUE;
2284     }
2285
2286     if (event->keyval == GDK_BackSpace)
2287         keycmd_bs(NULL, NULL, NULL);
2288
2289     gboolean key_ret = FALSE;
2290     if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2291         key_ret = TRUE;
2292     if (!key_ret) {
2293         GString* keycmd = g_string_new(uzbl.state.keycmd);
2294         g_string_append(keycmd, event->string);
2295         uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2296     }
2297
2298     run_keycmd(key_ret);
2299     update_title();
2300     if (key_ret) return (!uzbl.behave.insert_mode);
2301     return TRUE;
2302 }
2303
2304 void
2305 run_keycmd(const gboolean key_ret) {
2306     /* run the keycmd immediately if it isn't incremental and doesn't take args */
2307     Action *act;
2308     if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2309         clear_keycmd();
2310         parse_command(act->name, act->param, NULL);
2311         return;
2312     }
2313
2314     /* try if it's an incremental keycmd or one that takes args, and run it */
2315     GString* short_keys = g_string_new ("");
2316     GString* short_keys_inc = g_string_new ("");
2317     guint i;
2318     guint len = strlen(uzbl.state.keycmd);
2319     for (i=0; i<len; i++) {
2320         g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2321         g_string_assign(short_keys_inc, short_keys->str);
2322         g_string_append_c(short_keys, '_');
2323         g_string_append_c(short_keys_inc, '*');
2324
2325         if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2326             /* run normal cmds only if return was pressed */
2327             exec_paramcmd(act, i);
2328             clear_keycmd();
2329             break;
2330         } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2331             if (key_ret)  /* just quit the incremental command on return */
2332                 clear_keycmd();
2333             else exec_paramcmd(act, i); /* otherwise execute the incremental */
2334             break;
2335         }
2336
2337         g_string_truncate(short_keys, short_keys->len - 1);
2338     }
2339     g_string_free (short_keys, TRUE);
2340     g_string_free (short_keys_inc, TRUE);
2341 }
2342
2343 void
2344 exec_paramcmd(const Action *act, const guint i) {
2345     GString *parampart = g_string_new (uzbl.state.keycmd);
2346     GString *actionname = g_string_new ("");
2347     GString *actionparam = g_string_new ("");
2348     g_string_erase (parampart, 0, i+1);
2349     if (act->name)
2350         g_string_printf (actionname, act->name, parampart->str);
2351     if (act->param)
2352         g_string_printf (actionparam, act->param, parampart->str);
2353     parse_command(actionname->str, actionparam->str, NULL);
2354     g_string_free(actionname, TRUE);
2355     g_string_free(actionparam, TRUE);
2356     g_string_free(parampart, TRUE);
2357 }
2358
2359
2360 void
2361 create_browser () {
2362     GUI *g = &uzbl.gui;
2363
2364     g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2365
2366     g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2367     g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2368     g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2369     g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2370     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2371     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2372     g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2373     g_signal_connect (G_OBJECT (g->web_view), "navigation-policy-decision-requested", G_CALLBACK (navigation_decision_cb), g->web_view);
2374     g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2375     g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2376     g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2377     g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2378 }
2379
2380 GtkWidget*
2381 create_mainbar () {
2382     GUI *g = &uzbl.gui;
2383
2384     g->mainbar = gtk_hbox_new (FALSE, 0);
2385
2386     /* keep a reference to the bar so we can re-pack it at runtime*/
2387     //sbar_ref = g_object_ref(g->mainbar);
2388
2389     g->mainbar_label = gtk_label_new ("");
2390     gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2391     gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2392     gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2393     gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2394     gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2395     g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2396     return g->mainbar;
2397 }
2398
2399 GtkWidget*
2400 create_window () {
2401     GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2402     gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2403     gtk_widget_set_name (window, "Uzbl browser");
2404     g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2405     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2406     g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2407
2408     return window;
2409 }
2410
2411 GtkPlug*
2412 create_plug () {
2413     GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2414     g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2415     g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2416
2417     return plug;
2418 }
2419
2420
2421 gchar**
2422 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2423     /*
2424       If actname is one that calls an external command, this function will inject
2425       newargs in front of the user-provided args in that command line.  They will
2426       come become after the body of the script (in sh) or after the name of
2427       the command to execute (in spawn).
2428       i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2429       spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2430
2431       The return value consist of two strings: the action (sh, ...) and its args.
2432
2433       If act is not one that calls an external command, then the given action merely
2434       gets duplicated.
2435     */
2436     GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2437     /* Arrr! Here be memory leaks */
2438     gchar *actdup = g_strdup(actname);
2439     g_array_append_val(rets, actdup);
2440
2441     if ((g_strcmp0(actname, "spawn") == 0) ||
2442         (g_strcmp0(actname, "sh") == 0) ||
2443         (g_strcmp0(actname, "sync_spawn") == 0) ||
2444         (g_strcmp0(actname, "sync_sh") == 0) ||
2445         (g_strcmp0(actname, "talk_to_socket") == 0)) {
2446         guint i;
2447         GString *a = g_string_new("");
2448         gchar **spawnparts = split_quoted(origargs, FALSE);
2449         g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2450         if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2451
2452         for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2453             if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2454
2455         g_array_append_val(rets, a->str);
2456         g_string_free(a, FALSE);
2457         g_strfreev(spawnparts);
2458     } else {
2459         gchar *origdup = g_strdup(origargs);
2460         g_array_append_val(rets, origdup);
2461     }
2462     return (gchar**)g_array_free(rets, FALSE);
2463 }
2464
2465 void
2466 run_handler (const gchar *act, const gchar *args) {
2467     /* Consider this code a temporary hack to make the handlers usable.
2468        In practice, all this splicing, injection, and reconstruction is
2469        inefficient, annoying and hard to manage.  Potential pitfalls arise
2470        when the handler specific args 1) are not quoted  (the handler
2471        callbacks should take care of this)  2) are quoted but interfere
2472        with the users' own quotation.  A more ideal solution is
2473        to refactor parse_command so that it doesn't just take a string
2474        and execute it; rather than that, we should have a function which
2475        returns the argument vector parsed from the string.  This vector
2476        could be modified (e.g. insert additional args into it) before
2477        passing it to the next function that actually executes it.  Though
2478        it still isn't perfect for chain actions..  will reconsider & re-
2479        factor when I have the time. -duc */
2480
2481     char **parts = g_strsplit(act, " ", 2);
2482     if (!parts) return;
2483     if (g_strcmp0(parts[0], "chain") == 0) {
2484         GString *newargs = g_string_new("");
2485         gchar **chainparts = split_quoted(parts[1], FALSE);
2486
2487         /* for every argument in the chain, inject the handler args
2488            and make sure the new parts are wrapped in quotes */
2489         gchar **cp = chainparts;
2490         gchar quot = '\'';
2491         gchar *quotless = NULL;
2492         gchar **spliced_quotless = NULL; // sigh -_-;
2493         gchar **inpart = NULL;
2494
2495         while (*cp) {
2496             if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2497                 quot = **cp;
2498                 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2499             } else quotless = g_strdup(*cp);
2500
2501             spliced_quotless = g_strsplit(quotless, " ", 2);
2502             inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2503             g_strfreev(spliced_quotless);
2504
2505             g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2506             g_free(quotless);
2507             g_strfreev(inpart);
2508             cp++;
2509         }
2510
2511         parse_command(parts[0], &(newargs->str[1]), NULL);
2512         g_string_free(newargs, TRUE);
2513         g_strfreev(chainparts);
2514
2515     } else {
2516         gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2517         parse_command(inparts[0], inparts[1], NULL);
2518         g_free(inparts[0]);
2519         g_free(inparts[1]);
2520     }
2521     g_strfreev(parts);
2522 }
2523
2524 void
2525 add_binding (const gchar *key, const gchar *act) {
2526     char **parts = g_strsplit(act, " ", 2);
2527     Action *action;
2528
2529     if (!parts)
2530         return;
2531
2532     //Debug:
2533     if (uzbl.state.verbose)
2534         printf ("Binding %-10s : %s\n", key, act);
2535     action = new_action(parts[0], parts[1]);
2536
2537     if (g_hash_table_remove (uzbl.bindings, key))
2538         g_warning ("Overwriting existing binding for \"%s\"", key);
2539     g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2540     g_strfreev(parts);
2541 }
2542
2543 /*@null@*/ gchar*
2544 get_xdg_var (XDG_Var xdg) {
2545     const gchar* actual_value = getenv (xdg.environmental);
2546     const gchar* home         = getenv ("HOME");
2547     gchar* return_value;
2548
2549     if (! actual_value || strcmp (actual_value, "") == 0) {
2550         if (xdg.default_value) {
2551             return_value = str_replace ("~", home, xdg.default_value);
2552         } else {
2553             return_value = NULL;
2554         }
2555     } else {
2556         return_value = str_replace("~", home, actual_value);
2557     }
2558
2559     return return_value;
2560 }
2561
2562 /*@null@*/ gchar*
2563 find_xdg_file (int xdg_type, const char* filename) {
2564     /* xdg_type = 0 => config
2565        xdg_type = 1 => data
2566        xdg_type = 2 => cache*/
2567
2568     gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2569     gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2570     g_free (xdgv);
2571
2572     gchar* temporary_string;
2573     char*  saveptr;
2574     char*  buf;
2575
2576     if (! file_exists (temporary_file) && xdg_type != 2) {
2577         buf = get_xdg_var (XDG[3 + xdg_type]);
2578         temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2579         g_free(buf);
2580
2581         while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2582             g_free (temporary_file);
2583             temporary_file = g_strconcat (temporary_string, filename, NULL);
2584         }
2585     }
2586
2587     //g_free (temporary_string); - segfaults.
2588
2589     if (file_exists (temporary_file)) {
2590         return temporary_file;
2591     } else {
2592         g_free(temporary_file);
2593         return NULL;
2594     }
2595 }
2596 void
2597 settings_init () {
2598     State *s = &uzbl.state;
2599     Network *n = &uzbl.net;
2600     int i;
2601     for (i = 0; default_config[i].command != NULL; i++) {
2602         parse_cmd_line(default_config[i].command, NULL);
2603     }
2604
2605     if (g_strcmp0(s->config_file, "-") == 0) {
2606         s->config_file = NULL;
2607         create_stdin();
2608     }
2609
2610     else if (!s->config_file) {
2611         s->config_file = find_xdg_file (0, "/uzbl/config");
2612     }
2613
2614     if (s->config_file) {
2615         GArray* lines = read_file_by_line (s->config_file);
2616         int i = 0;
2617         gchar* line;
2618
2619         while ((line = g_array_index(lines, gchar*, i))) {
2620             parse_cmd_line (line, NULL);
2621             i ++;
2622             g_free (line);
2623         }
2624         g_array_free (lines, TRUE);
2625     } else {
2626         if (uzbl.state.verbose)
2627             printf ("No configuration file loaded.\n");
2628     }
2629
2630     g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2631 }
2632
2633 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2634     (void) session;
2635     (void) user_data;
2636     if (!uzbl.behave.cookie_handler)
2637          return;
2638
2639     soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2640     GString *s = g_string_new ("");
2641     SoupURI * soup_uri = soup_message_get_uri(msg);
2642     g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2643     run_handler(uzbl.behave.cookie_handler, s->str);
2644
2645     if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2646         char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2647         if ( p != NULL ) *p = '\0';
2648         soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2649     }
2650     if (uzbl.comm.sync_stdout)
2651         uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2652
2653     g_string_free(s, TRUE);
2654 }
2655
2656 void
2657 save_cookies (SoupMessage *msg, gpointer user_data){
2658     (void) user_data;
2659     GSList *ck;
2660     char *cookie;
2661     for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2662         cookie = soup_cookie_to_set_cookie_header(ck->data);
2663         SoupURI * soup_uri = soup_message_get_uri(msg);
2664         GString *s = g_string_new ("");
2665         g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2666         run_handler(uzbl.behave.cookie_handler, s->str);
2667         g_free (cookie);
2668         g_string_free(s, TRUE);
2669     }
2670     g_slist_free(ck);
2671 }
2672
2673 /* --- WEBINSPECTOR --- */
2674 void
2675 hide_window_cb(GtkWidget *widget, gpointer data) {
2676     (void) data;
2677
2678     gtk_widget_hide(widget);
2679 }
2680
2681 WebKitWebView*
2682 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2683     (void) data;
2684     (void) page;
2685     (void) web_inspector;
2686     GtkWidget* scrolled_window;
2687     GtkWidget* new_web_view;
2688     GUI *g = &uzbl.gui;
2689
2690     g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2691     g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2692             G_CALLBACK(hide_window_cb), NULL);
2693
2694     gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2695     gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2696     gtk_widget_show(g->inspector_window);
2697
2698     scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2699     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2700             GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2701     gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2702     gtk_widget_show(scrolled_window);
2703
2704     new_web_view = webkit_web_view_new();
2705     gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2706
2707     return WEBKIT_WEB_VIEW(new_web_view);
2708 }
2709
2710 gboolean
2711 inspector_show_window_cb (WebKitWebInspector* inspector){
2712     (void) inspector;
2713     gtk_widget_show(uzbl.gui.inspector_window);
2714     return TRUE;
2715 }
2716
2717 /* TODO: Add variables and code to make use of these functions */
2718 gboolean
2719 inspector_close_window_cb (WebKitWebInspector* inspector){
2720     (void) inspector;
2721     return TRUE;
2722 }
2723
2724 gboolean
2725 inspector_attach_window_cb (WebKitWebInspector* inspector){
2726     (void) inspector;
2727     return FALSE;
2728 }
2729
2730 gboolean
2731 inspector_detach_window_cb (WebKitWebInspector* inspector){
2732     (void) inspector;
2733     return FALSE;
2734 }
2735
2736 gboolean
2737 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2738     (void) inspector;
2739     return FALSE;
2740 }
2741
2742 gboolean
2743 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2744     (void) inspector;
2745     return FALSE;
2746 }
2747
2748 void
2749 set_up_inspector() {
2750     GUI *g = &uzbl.gui;
2751     WebKitWebSettings *settings = view_settings();
2752     g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2753
2754     uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2755     g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2756     g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2757     g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2758     g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2759     g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2760     g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2761
2762     g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2763 }
2764
2765 void
2766 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2767     (void) ud;
2768     uzbl_cmdprop *c = v;
2769
2770     if(!c->dump)
2771         return;
2772
2773     if(c->type == TYPE_STR)
2774         printf("set %s = %s\n", (char *)k, *c->ptr.s ? *c->ptr.s : " ");
2775     else if(c->type == TYPE_INT)
2776         printf("set %s = %d\n", (char *)k, *c->ptr.i);
2777     else if(c->type == TYPE_FLOAT)
2778         printf("set %s = %f\n", (char *)k, *c->ptr.f);
2779 }
2780
2781 void
2782 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2783     (void) ud;
2784     Action *a = v;
2785
2786     printf("bind %s = %s %s\n", (char *)k ,
2787             (char *)a->name, a->param?(char *)a->param:"");
2788 }
2789
2790 void
2791 dump_config() {
2792     g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2793     g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2794 }
2795
2796 void
2797 retrieve_geometry() {
2798     int w, h, x, y;
2799     GString *buf = g_string_new("");
2800
2801     gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2802     gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2803
2804     g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2805
2806     if(uzbl.gui.geometry)
2807         g_free(uzbl.gui.geometry);
2808     uzbl.gui.geometry = g_string_free(buf, FALSE);
2809 }
2810
2811 /* set up gtk, gobject, variable defaults and other things that tests and other
2812  * external applications need to do anyhow */
2813 void
2814 initialize(int argc, char *argv[]) {
2815     if (!g_thread_supported ())
2816         g_thread_init (NULL);
2817     uzbl.state.executable_path = g_strdup(argv[0]);
2818     uzbl.state.selected_url = NULL;
2819     uzbl.state.searchtx = NULL;
2820
2821     GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2822     g_option_context_add_main_entries (context, entries, NULL);
2823     g_option_context_add_group (context, gtk_get_option_group (TRUE));
2824     g_option_context_parse (context, &argc, &argv, NULL);
2825     g_option_context_free(context);
2826
2827     if (uzbl.behave.print_version) {
2828         printf("Commit: %s\n", COMMIT);
2829         exit(EXIT_SUCCESS);
2830     }
2831
2832     /* initialize hash table */
2833     uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2834
2835     uzbl.net.soup_session = webkit_get_default_session();
2836     uzbl.state.keycmd = g_strdup("");
2837
2838     if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2839         fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2840     if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2841         fprintf(stderr, "uzbl: error hooking SIGINT\n");
2842
2843     uzbl.gui.sbar.progress_s = g_strdup("="); //TODO: move these to config.h
2844     uzbl.gui.sbar.progress_u = g_strdup("ยท");
2845     uzbl.gui.sbar.progress_w = 10;
2846
2847     /* default mode indicators */
2848     uzbl.behave.insert_indicator = g_strdup("I");
2849     uzbl.behave.cmd_indicator    = g_strdup("C");
2850
2851     uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2852     uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2853     uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2854     uzbl.info.arch         = ARCH;
2855     uzbl.info.commit       = COMMIT;
2856
2857     commands_hash ();
2858     make_var_to_name_hash();
2859
2860     create_browser();
2861 }
2862
2863 #ifndef UZBL_LIBRARY
2864 /** -- MAIN -- **/
2865 int
2866 main (int argc, char* argv[]) {
2867     initialize(argc, argv);
2868
2869     gtk_init (&argc, &argv);
2870
2871     uzbl.gui.scrolled_win = gtk_scrolled_window_new (NULL, NULL);
2872     //main_window_ref = g_object_ref(scrolled_window);
2873     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (uzbl.gui.scrolled_win),
2874         GTK_POLICY_NEVER, GTK_POLICY_NEVER); //todo: some sort of display of position/total length. like what emacs does
2875
2876     gtk_container_add (GTK_CONTAINER (uzbl.gui.scrolled_win),
2877         GTK_WIDGET (uzbl.gui.web_view));
2878
2879     uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2880
2881     create_mainbar();
2882
2883     /* initial packing */
2884     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2885     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2886
2887     if (uzbl.state.socket_id) {
2888         uzbl.gui.plug = create_plug ();
2889         gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2890         gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2891     } else {
2892         uzbl.gui.main_window = create_window ();
2893         gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2894         gtk_widget_show_all (uzbl.gui.main_window);
2895         uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2896     }
2897
2898     if(!uzbl.state.instance_name)
2899         uzbl.state.instance_name = itos((int)uzbl.xwin);
2900
2901     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2902
2903     if (uzbl.state.verbose) {
2904         printf("Uzbl start location: %s\n", argv[0]);
2905         if (uzbl.state.socket_id)
2906             printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2907         else
2908             printf("window_id %i\n",(int) uzbl.xwin);
2909         printf("pid %i\n", getpid ());
2910         printf("name: %s\n", uzbl.state.instance_name);
2911         printf("commit: %s\n", uzbl.info.commit);
2912     }
2913
2914     uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2915     uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2916     uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2917     uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2918     gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2919
2920     /* Check uzbl is in window mode before getting/setting geometry */
2921     if (uzbl.gui.main_window) {
2922         if(uzbl.gui.geometry)
2923             cmd_set_geometry();
2924         else
2925             retrieve_geometry();
2926     }
2927
2928     gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2929     if (argc > 1 && !uzbl.state.uri)
2930         uri_override = g_strdup(argv[1]);
2931     gboolean verbose_override = uzbl.state.verbose;
2932
2933     settings_init ();
2934     set_insert_mode(FALSE);
2935
2936     if (!uzbl.behave.show_status)
2937         gtk_widget_hide(uzbl.gui.mainbar);
2938     else
2939         update_title();
2940
2941     /* WebInspector */
2942     set_up_inspector();
2943
2944     if (verbose_override > uzbl.state.verbose)
2945         uzbl.state.verbose = verbose_override;
2946
2947     if (uri_override) {
2948         set_var_value("uri", uri_override);
2949         g_free(uri_override);
2950     } else if (uzbl.state.uri)
2951         cmd_load_uri();
2952
2953     gtk_main ();
2954     clean_up();
2955
2956     return EXIT_SUCCESS;
2957 }
2958 #endif
2959
2960 /* vi: set et ts=4: */