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