fix malloc() bug
[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 = malloc(sizeof(char *));
1748         *c->ptr = buf;
1749         g_hash_table_insert(uzbl.comm.proto_var,
1750                 g_strdup(name), (gpointer) c);
1751     }
1752     return TRUE;
1753 }
1754
1755 void
1756 render_html() {
1757     Behaviour *b = &uzbl.behave;
1758
1759     if(b->html_buffer->str) {
1760         webkit_web_view_load_html_string (uzbl.gui.web_view,
1761                 b->html_buffer->str, b->base_url);
1762         g_string_free(b->html_buffer, TRUE);
1763         b->html_buffer = g_string_new("");
1764     }
1765 }
1766
1767 enum {M_CMD, M_HTML};
1768 void
1769 parse_cmd_line(const char *ctl_line, GString *result) {
1770     Behaviour *b = &uzbl.behave;
1771     size_t len=0;
1772
1773     if(b->mode == M_HTML) {
1774         len = strlen(b->html_endmarker);
1775         /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1776         if(len == strlen(ctl_line)-1 &&
1777            !strncmp(b->html_endmarker, ctl_line, len)) {
1778             set_timeout(0);
1779             set_var_value("mode", "0");
1780             render_html();
1781             return;
1782         }
1783         else {
1784             set_timeout(b->html_timeout);
1785             g_string_append(b->html_buffer, ctl_line);
1786         }
1787     }
1788     else if((ctl_line[0] == '#') /* Comments */
1789             || (ctl_line[0] == ' ')
1790             || (ctl_line[0] == '\n'))
1791         ; /* ignore these lines */
1792     else { /* parse a command */
1793         gchar *ctlstrip;
1794         gchar **tokens = NULL;
1795         len = strlen(ctl_line);
1796
1797         if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1798             ctlstrip = g_strndup(ctl_line, len - 1);
1799         else ctlstrip = g_strdup(ctl_line);
1800
1801         tokens = g_strsplit(ctlstrip, " ", 2);
1802         parse_command(tokens[0], tokens[1], result);
1803         g_free(ctlstrip);
1804         g_strfreev(tokens);
1805     }
1806 }
1807
1808 gchar*
1809 build_stream_name(int type, const gchar* dir) {
1810     State *s = &uzbl.state;
1811     gchar *str = NULL;
1812
1813     if (type == FIFO) {
1814         str = g_strdup_printf
1815             ("%s/uzbl_fifo_%s", dir, s->instance_name);
1816     } else if (type == SOCKET) {
1817         str = g_strdup_printf
1818             ("%s/uzbl_socket_%s", dir, s->instance_name);
1819     }
1820     return str;
1821 }
1822
1823 gboolean
1824 control_fifo(GIOChannel *gio, GIOCondition condition) {
1825     if (uzbl.state.verbose)
1826         printf("triggered\n");
1827     gchar *ctl_line;
1828     GIOStatus ret;
1829     GError *err = NULL;
1830
1831     if (condition & G_IO_HUP)
1832         g_error ("Fifo: Read end of pipe died!\n");
1833
1834     if(!gio)
1835        g_error ("Fifo: GIOChannel broke\n");
1836
1837     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1838     if (ret == G_IO_STATUS_ERROR) {
1839         g_error ("Fifo: Error reading: %s\n", err->message);
1840         g_error_free (err);
1841     }
1842
1843     parse_cmd_line(ctl_line, NULL);
1844     g_free(ctl_line);
1845
1846     return TRUE;
1847 }
1848
1849 gchar*
1850 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1851     if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1852         if (unlink(uzbl.comm.fifo_path) == -1)
1853             g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1854         g_free(uzbl.comm.fifo_path);
1855         uzbl.comm.fifo_path = NULL;
1856     }
1857
1858     GIOChannel *chan = NULL;
1859     GError *error = NULL;
1860     gchar *path = build_stream_name(FIFO, dir);
1861
1862     if (!file_exists(path)) {
1863         if (mkfifo (path, 0666) == 0) {
1864             // 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.
1865             chan = g_io_channel_new_file(path, "r+", &error);
1866             if (chan) {
1867                 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1868                     if (uzbl.state.verbose)
1869                         printf ("init_fifo: created successfully as %s\n", path);
1870                     uzbl.comm.fifo_path = path;
1871                     return dir;
1872                 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1873             } else g_warning ("init_fifo: can't open: %s\n", error->message);
1874         } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1875     } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1876
1877     /* if we got this far, there was an error; cleanup */
1878     if (error) g_error_free (error);
1879     g_free(dir);
1880     g_free(path);
1881     return NULL;
1882 }
1883
1884 gboolean
1885 control_stdin(GIOChannel *gio, GIOCondition condition) {
1886     (void) condition;
1887     gchar *ctl_line = NULL;
1888     GIOStatus ret;
1889
1890     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1891     if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1892         return FALSE;
1893
1894     parse_cmd_line(ctl_line, NULL);
1895     g_free(ctl_line);
1896
1897     return TRUE;
1898 }
1899
1900 void
1901 create_stdin () {
1902     GIOChannel *chan = NULL;
1903     GError *error = NULL;
1904
1905     chan = g_io_channel_unix_new(fileno(stdin));
1906     if (chan) {
1907         if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1908             g_error ("Stdin: could not add watch\n");
1909         } else {
1910             if (uzbl.state.verbose)
1911                 printf ("Stdin: watch added successfully\n");
1912         }
1913     } else {
1914         g_error ("Stdin: Error while opening: %s\n", error->message);
1915     }
1916     if (error) g_error_free (error);
1917 }
1918
1919 gboolean
1920 control_socket(GIOChannel *chan) {
1921     struct sockaddr_un remote;
1922     unsigned int t = sizeof(remote);
1923     int clientsock;
1924     GIOChannel *clientchan;
1925
1926     clientsock = accept (g_io_channel_unix_get_fd(chan),
1927                          (struct sockaddr *) &remote, &t);
1928
1929     if ((clientchan = g_io_channel_unix_new(clientsock))) {
1930         g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1931                        (GIOFunc) control_client_socket, clientchan);
1932     }
1933
1934     return TRUE;
1935 }
1936
1937 gboolean
1938 control_client_socket(GIOChannel *clientchan) {
1939     char *ctl_line;
1940     GString *result = g_string_new("");
1941     GError *error = NULL;
1942     GIOStatus ret;
1943     gsize len;
1944
1945     ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1946     if (ret == G_IO_STATUS_ERROR) {
1947         g_warning ("Error reading: %s\n", error->message);
1948         g_io_channel_shutdown(clientchan, TRUE, &error);
1949         return FALSE;
1950     } else if (ret == G_IO_STATUS_EOF) {
1951         /* shutdown and remove channel watch from main loop */
1952         g_io_channel_shutdown(clientchan, TRUE, &error);
1953         return FALSE;
1954     }
1955
1956     if (ctl_line) {
1957         parse_cmd_line (ctl_line, result);
1958         g_string_append_c(result, '\n');
1959         ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1960                                         &len, &error);
1961         if (ret == G_IO_STATUS_ERROR) {
1962             g_warning ("Error writing: %s", error->message);
1963         }
1964         g_io_channel_flush(clientchan, &error);
1965     }
1966
1967     if (error) g_error_free (error);
1968     g_string_free(result, TRUE);
1969     g_free(ctl_line);
1970     return TRUE;
1971 }
1972
1973 gchar*
1974 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1975     if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1976         if (unlink(uzbl.comm.socket_path) == -1)
1977             g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1978         g_free(uzbl.comm.socket_path);
1979         uzbl.comm.socket_path = NULL;
1980     }
1981
1982     if (*dir == ' ') {
1983         g_free(dir);
1984         return NULL;
1985     }
1986
1987     GIOChannel *chan = NULL;
1988     int sock, len;
1989     struct sockaddr_un local;
1990     gchar *path = build_stream_name(SOCKET, dir);
1991
1992     sock = socket (AF_UNIX, SOCK_STREAM, 0);
1993
1994     local.sun_family = AF_UNIX;
1995     strcpy (local.sun_path, path);
1996     unlink (local.sun_path);
1997
1998     len = strlen (local.sun_path) + sizeof (local.sun_family);
1999     if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2000         if (uzbl.state.verbose)
2001             printf ("init_socket: opened in %s\n", path);
2002         listen (sock, 5);
2003
2004         if( (chan = g_io_channel_unix_new(sock)) ) {
2005             g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2006             uzbl.comm.socket_path = path;
2007             return dir;
2008         }
2009     } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2010
2011     /* if we got this far, there was an error; cleanup */
2012     g_free(path);
2013     g_free(dir);
2014     return NULL;
2015 }
2016
2017 /*
2018  NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2019  it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2020 */
2021 // this function may be called very early when the templates are not set (yet), hence the checks
2022 void
2023 update_title (void) {
2024     Behaviour *b = &uzbl.behave;
2025     gchar *parsed;
2026
2027     if (b->show_status) {
2028         if (b->title_format_short) {
2029             parsed = expand(b->title_format_short, 0);
2030             if (uzbl.gui.main_window)
2031                 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2032             g_free(parsed);
2033         }
2034         if (b->status_format) {
2035             parsed = expand(b->status_format, 0);
2036             gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2037             g_free(parsed);
2038         }
2039         if (b->status_background) {
2040             GdkColor color;
2041             gdk_color_parse (b->status_background, &color);
2042             //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)
2043             if (uzbl.gui.main_window)
2044                 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2045             else if (uzbl.gui.plug)
2046                 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2047         }
2048     } else {
2049         if (b->title_format_long) {
2050             parsed = expand(b->title_format_long, 0);
2051             if (uzbl.gui.main_window)
2052                 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2053             g_free(parsed);
2054         }
2055     }
2056 }
2057
2058 gboolean
2059 configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
2060     (void) window;
2061     (void) event;
2062
2063     retrieve_geometry();
2064     return FALSE;
2065 }
2066
2067 gboolean
2068 key_press_cb (GtkWidget* window, GdkEventKey* event)
2069 {
2070     //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2071
2072     (void) window;
2073
2074     if (event->type   != GDK_KEY_PRESS ||
2075         event->keyval == GDK_Page_Up   ||
2076         event->keyval == GDK_Page_Down ||
2077         event->keyval == GDK_Up        ||
2078         event->keyval == GDK_Down      ||
2079         event->keyval == GDK_Left      ||
2080         event->keyval == GDK_Right     ||
2081         event->keyval == GDK_Shift_L   ||
2082         event->keyval == GDK_Shift_R)
2083         return FALSE;
2084
2085     /* turn off insert mode (if always_insert_mode is not used) */
2086     if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2087         set_insert_mode(uzbl.behave.always_insert_mode);
2088         update_title();
2089         return TRUE;
2090     }
2091
2092     if (uzbl.behave.insert_mode &&
2093         ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
2094           (!uzbl.behave.modmask)
2095         )
2096        )
2097         return FALSE;
2098
2099     if (event->keyval == GDK_Escape) {
2100         clear_keycmd();
2101         update_title();
2102         dehilight(uzbl.gui.web_view, NULL, NULL);
2103         return TRUE;
2104     }
2105
2106     //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2107     if (event->keyval == GDK_Insert) {
2108         gchar * str;
2109         if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2110             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2111         } else {
2112             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2113         }
2114         if (str) {
2115             GString* keycmd = g_string_new(uzbl.state.keycmd);
2116             g_string_append (keycmd, str);
2117             uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2118             update_title ();
2119             g_free (str);
2120         }
2121         return TRUE;
2122     }
2123
2124     if (event->keyval == GDK_BackSpace)
2125         keycmd_bs(NULL, NULL, NULL);
2126
2127     gboolean key_ret = FALSE;
2128     if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2129         key_ret = TRUE;
2130     if (!key_ret) {
2131         GString* keycmd = g_string_new(uzbl.state.keycmd);
2132         g_string_append(keycmd, event->string);
2133         uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2134     }
2135
2136     run_keycmd(key_ret);
2137     update_title();
2138     if (key_ret) return (!uzbl.behave.insert_mode);
2139     return TRUE;
2140 }
2141
2142 void
2143 run_keycmd(const gboolean key_ret) {
2144     /* run the keycmd immediately if it isn't incremental and doesn't take args */
2145     Action *act;
2146     if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2147         clear_keycmd();
2148         parse_command(act->name, act->param, NULL);
2149         return;
2150     }
2151
2152     /* try if it's an incremental keycmd or one that takes args, and run it */
2153     GString* short_keys = g_string_new ("");
2154     GString* short_keys_inc = g_string_new ("");
2155     guint i;
2156     guint len = strlen(uzbl.state.keycmd);
2157     for (i=0; i<len; i++) {
2158         g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2159         g_string_assign(short_keys_inc, short_keys->str);
2160         g_string_append_c(short_keys, '_');
2161         g_string_append_c(short_keys_inc, '*');
2162
2163         if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2164             /* run normal cmds only if return was pressed */
2165             exec_paramcmd(act, i);
2166             clear_keycmd();
2167             break;
2168         } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2169             if (key_ret)  /* just quit the incremental command on return */
2170                 clear_keycmd();
2171             else exec_paramcmd(act, i); /* otherwise execute the incremental */
2172             break;
2173         }
2174
2175         g_string_truncate(short_keys, short_keys->len - 1);
2176     }
2177     g_string_free (short_keys, TRUE);
2178     g_string_free (short_keys_inc, TRUE);
2179 }
2180
2181 void
2182 exec_paramcmd(const Action *act, const guint i) {
2183     GString *parampart = g_string_new (uzbl.state.keycmd);
2184     GString *actionname = g_string_new ("");
2185     GString *actionparam = g_string_new ("");
2186     g_string_erase (parampart, 0, i+1);
2187     if (act->name)
2188         g_string_printf (actionname, act->name, parampart->str);
2189     if (act->param)
2190         g_string_printf (actionparam, act->param, parampart->str);
2191     parse_command(actionname->str, actionparam->str, NULL);
2192     g_string_free(actionname, TRUE);
2193     g_string_free(actionparam, TRUE);
2194     g_string_free(parampart, TRUE);
2195 }
2196
2197
2198 void
2199 create_browser () {
2200     GUI *g = &uzbl.gui;
2201
2202     g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2203
2204     g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2205     g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2206     g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2207     g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2208     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2209     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2210     g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2211     g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2212     g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2213     g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2214     g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2215 }
2216
2217 GtkWidget*
2218 create_mainbar () {
2219     GUI *g = &uzbl.gui;
2220
2221     g->mainbar = gtk_hbox_new (FALSE, 0);
2222
2223     /* keep a reference to the bar so we can re-pack it at runtime*/
2224     //sbar_ref = g_object_ref(g->mainbar);
2225
2226     g->mainbar_label = gtk_label_new ("");
2227     gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2228     gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2229     gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2230     gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2231     gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2232     g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2233     return g->mainbar;
2234 }
2235
2236 GtkWidget*
2237 create_window () {
2238     GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2239     gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2240     gtk_widget_set_name (window, "Uzbl browser");
2241     g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2242     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2243     g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2244
2245     return window;
2246 }
2247
2248 GtkPlug*
2249 create_plug () {
2250     GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2251     g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2252     g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2253
2254     return plug;
2255 }
2256
2257
2258 gchar**
2259 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2260     /*
2261       If actname is one that calls an external command, this function will inject
2262       newargs in front of the user-provided args in that command line.  They will
2263       come become after the body of the script (in sh) or after the name of
2264       the command to execute (in spawn).
2265       i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2266       spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2267
2268       The return value consist of two strings: the action (sh, ...) and its args.
2269
2270       If act is not one that calls an external command, then the given action merely
2271       gets duplicated.
2272     */
2273     GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2274     /* Arrr! Here be memory leaks */
2275     gchar *actdup = g_strdup(actname);
2276     g_array_append_val(rets, actdup);
2277
2278     if ((g_strcmp0(actname, "spawn") == 0) ||
2279         (g_strcmp0(actname, "sh") == 0) ||
2280         (g_strcmp0(actname, "sync_spawn") == 0) ||
2281         (g_strcmp0(actname, "sync_sh") == 0)) {
2282         guint i;
2283         GString *a = g_string_new("");
2284         gchar **spawnparts = split_quoted(origargs, FALSE);
2285         g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2286         if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2287
2288         for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2289             if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2290
2291         g_array_append_val(rets, a->str);
2292         g_string_free(a, FALSE);
2293         g_strfreev(spawnparts);
2294     } else {
2295         gchar *origdup = g_strdup(origargs);
2296         g_array_append_val(rets, origdup);
2297     }
2298     return (gchar**)g_array_free(rets, FALSE);
2299 }
2300
2301 void
2302 run_handler (const gchar *act, const gchar *args) {
2303     /* Consider this code a temporary hack to make the handlers usable.
2304        In practice, all this splicing, injection, and reconstruction is
2305        inefficient, annoying and hard to manage.  Potential pitfalls arise
2306        when the handler specific args 1) are not quoted  (the handler
2307        callbacks should take care of this)  2) are quoted but interfere
2308        with the users' own quotation.  A more ideal solution is
2309        to refactor parse_command so that it doesn't just take a string
2310        and execute it; rather than that, we should have a function which
2311        returns the argument vector parsed from the string.  This vector
2312        could be modified (e.g. insert additional args into it) before
2313        passing it to the next function that actually executes it.  Though
2314        it still isn't perfect for chain actions..  will reconsider & re-
2315        factor when I have the time. -duc */
2316
2317     char **parts = g_strsplit(act, " ", 2);
2318     if (!parts) return;
2319     if (g_strcmp0(parts[0], "chain") == 0) {
2320         GString *newargs = g_string_new("");
2321         gchar **chainparts = split_quoted(parts[1], FALSE);
2322
2323         /* for every argument in the chain, inject the handler args
2324            and make sure the new parts are wrapped in quotes */
2325         gchar **cp = chainparts;
2326         gchar quot = '\'';
2327         gchar *quotless = NULL;
2328         gchar **spliced_quotless = NULL; // sigh -_-;
2329         gchar **inpart = NULL;
2330
2331         while (*cp) {
2332             if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2333                 quot = **cp;
2334                 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2335             } else quotless = g_strdup(*cp);
2336
2337             spliced_quotless = g_strsplit(quotless, " ", 2);
2338             inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2339             g_strfreev(spliced_quotless);
2340
2341             g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2342             g_free(quotless);
2343             g_strfreev(inpart);
2344             cp++;
2345         }
2346
2347         parse_command(parts[0], &(newargs->str[1]), NULL);
2348         g_string_free(newargs, TRUE);
2349         g_strfreev(chainparts);
2350
2351     } else {
2352         gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2353         parse_command(inparts[0], inparts[1], NULL);
2354         g_free(inparts[0]);
2355         g_free(inparts[1]);
2356     }
2357     g_strfreev(parts);
2358 }
2359
2360 void
2361 add_binding (const gchar *key, const gchar *act) {
2362     char **parts = g_strsplit(act, " ", 2);
2363     Action *action;
2364
2365     if (!parts)
2366         return;
2367
2368     //Debug:
2369     if (uzbl.state.verbose)
2370         printf ("Binding %-10s : %s\n", key, act);
2371     action = new_action(parts[0], parts[1]);
2372
2373     if (g_hash_table_remove (uzbl.bindings, key))
2374         g_warning ("Overwriting existing binding for \"%s\"", key);
2375     g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2376     g_strfreev(parts);
2377 }
2378
2379 gchar*
2380 get_xdg_var (XDG_Var xdg) {
2381     const gchar* actual_value = getenv (xdg.environmental);
2382     const gchar* home         = getenv ("HOME");
2383     gchar* return_value;
2384
2385     if (! actual_value || strcmp (actual_value, "") == 0) {
2386         if (xdg.default_value) {
2387             return_value = str_replace ("~", home, xdg.default_value);
2388         } else {
2389             return_value = NULL;
2390         }
2391     } else {
2392         return_value = str_replace("~", home, actual_value);
2393     }
2394
2395     return return_value;
2396 }
2397
2398 gchar*
2399 find_xdg_file (int xdg_type, char* filename) {
2400     /* xdg_type = 0 => config
2401        xdg_type = 1 => data
2402        xdg_type = 2 => cache*/
2403
2404     gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2405     gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2406     g_free (xdgv);
2407
2408     gchar* temporary_string;
2409     char*  saveptr;
2410     char*  buf;
2411
2412     if (! file_exists (temporary_file) && xdg_type != 2) {
2413         buf = get_xdg_var (XDG[3 + xdg_type]);
2414         temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2415         g_free(buf);
2416
2417         while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2418             g_free (temporary_file);
2419             temporary_file = g_strconcat (temporary_string, filename, NULL);
2420         }
2421     }
2422
2423     //g_free (temporary_string); - segfaults.
2424
2425     if (file_exists (temporary_file)) {
2426         return temporary_file;
2427     } else {
2428         return NULL;
2429     }
2430 }
2431 void
2432 settings_init () {
2433     State *s = &uzbl.state;
2434     Network *n = &uzbl.net;
2435     int i;
2436     for (i = 0; default_config[i].command != NULL; i++) {
2437         parse_cmd_line(default_config[i].command, NULL);
2438     }
2439
2440     if (g_strcmp0(s->config_file, "-") == 0) {
2441         s->config_file = NULL;
2442         create_stdin();
2443     }
2444
2445     else if (!s->config_file) {
2446         s->config_file = find_xdg_file (0, "/uzbl/config");
2447     }
2448
2449     if (s->config_file) {
2450         GArray* lines = read_file_by_line (s->config_file);
2451         int i = 0;
2452         gchar* line;
2453
2454         while ((line = g_array_index(lines, gchar*, i))) {
2455             parse_cmd_line (line, NULL);
2456             i ++;
2457             g_free (line);
2458         }
2459         g_array_free (lines, TRUE);
2460     } else {
2461         if (uzbl.state.verbose)
2462             printf ("No configuration file loaded.\n");
2463     }
2464
2465     g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2466 }
2467
2468 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2469     (void) session;
2470     (void) user_data;
2471     if (!uzbl.behave.cookie_handler)
2472          return;
2473
2474     soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2475     GString *s = g_string_new ("");
2476     SoupURI * soup_uri = soup_message_get_uri(msg);
2477     g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2478     run_handler(uzbl.behave.cookie_handler, s->str);
2479
2480     if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2481         char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2482         if ( p != NULL ) *p = '\0';
2483         soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2484     }
2485     if (uzbl.comm.sync_stdout)
2486         uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2487
2488     g_string_free(s, TRUE);
2489 }
2490
2491 void
2492 save_cookies (SoupMessage *msg, gpointer user_data){
2493     (void) user_data;
2494     GSList *ck;
2495     char *cookie;
2496     for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2497         cookie = soup_cookie_to_set_cookie_header(ck->data);
2498         SoupURI * soup_uri = soup_message_get_uri(msg);
2499         GString *s = g_string_new ("");
2500         g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2501         run_handler(uzbl.behave.cookie_handler, s->str);
2502         g_free (cookie);
2503         g_string_free(s, TRUE);
2504     }
2505     g_slist_free(ck);
2506 }
2507
2508 /* --- WEBINSPECTOR --- */
2509 void
2510 hide_window_cb(GtkWidget *widget, gpointer data) {
2511     (void) data;
2512
2513     gtk_widget_hide(widget);
2514 }
2515
2516 WebKitWebView*
2517 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2518     (void) data;
2519     (void) page;
2520     (void) web_inspector;
2521     GtkWidget* scrolled_window;
2522     GtkWidget* new_web_view;
2523     GUI *g = &uzbl.gui;
2524
2525     g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2526     g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2527             G_CALLBACK(hide_window_cb), NULL);
2528
2529     gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2530     gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2531     gtk_widget_show(g->inspector_window);
2532
2533     scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2534     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2535             GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2536     gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2537     gtk_widget_show(scrolled_window);
2538
2539     new_web_view = webkit_web_view_new();
2540     gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2541
2542     return WEBKIT_WEB_VIEW(new_web_view);
2543 }
2544
2545 gboolean
2546 inspector_show_window_cb (WebKitWebInspector* inspector){
2547     (void) inspector;
2548     gtk_widget_show(uzbl.gui.inspector_window);
2549     return TRUE;
2550 }
2551
2552 /* TODO: Add variables and code to make use of these functions */
2553 gboolean
2554 inspector_close_window_cb (WebKitWebInspector* inspector){
2555     (void) inspector;
2556     return TRUE;
2557 }
2558
2559 gboolean
2560 inspector_attach_window_cb (WebKitWebInspector* inspector){
2561     (void) inspector;
2562     return FALSE;
2563 }
2564
2565 gboolean
2566 inspector_detach_window_cb (WebKitWebInspector* inspector){
2567     (void) inspector;
2568     return FALSE;
2569 }
2570
2571 gboolean
2572 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2573     (void) inspector;
2574     return FALSE;
2575 }
2576
2577 gboolean
2578 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2579     (void) inspector;
2580     return FALSE;
2581 }
2582
2583 void
2584 set_up_inspector() {
2585     GUI *g = &uzbl.gui;
2586     WebKitWebSettings *settings = view_settings();
2587     g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2588
2589     uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2590     g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2591     g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2592     g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2593     g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2594     g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2595     g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2596
2597     g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2598 }
2599
2600 void
2601 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2602     (void) ud;
2603     uzbl_cmdprop *c = v;
2604
2605     if(!c->dump)
2606         return;
2607
2608     if(c->type == TYPE_STR)
2609         printf("set %s = %s\n", (char *)k, *c->ptr ? (char *)*c->ptr : " ");
2610     else if(c->type == TYPE_INT)
2611         printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2612     else if(c->type == TYPE_FLOAT)
2613         printf("set %s = %f\n", (char *)k, *(float *)c->ptr);
2614 }
2615
2616 void
2617 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2618     (void) ud;
2619     Action *a = v;
2620
2621     printf("bind %s = %s %s\n", (char *)k ,
2622             (char *)a->name, a->param?(char *)a->param:"");
2623 }
2624
2625 void
2626 dump_config() {
2627     g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2628     g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2629 }
2630
2631 void
2632 retrieve_geometry() {
2633     int w, h, x, y;
2634     GString *buf = g_string_new("");
2635
2636     gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2637     gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2638
2639     g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2640
2641     if(uzbl.gui.geometry)
2642         g_free(uzbl.gui.geometry);
2643     uzbl.gui.geometry = g_string_free(buf, FALSE);
2644 }
2645
2646 /* set up gtk, gobject, variable defaults and other things that tests and other
2647  * external applications need to do anyhow */
2648 void
2649 initialize(int argc, char *argv[]) {
2650     if (!g_thread_supported ())
2651         g_thread_init (NULL);
2652     uzbl.state.executable_path = g_strdup(argv[0]);
2653     uzbl.state.selected_url = NULL;
2654     uzbl.state.searchtx = NULL;
2655
2656     GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2657     g_option_context_add_main_entries (context, entries, NULL);
2658     g_option_context_add_group (context, gtk_get_option_group (TRUE));
2659     g_option_context_parse (context, &argc, &argv, NULL);
2660     g_option_context_free(context);
2661
2662     if (uzbl.behave.print_version) {
2663         printf("Commit: %s\n", COMMIT);
2664         exit(0);
2665     }
2666
2667     /* initialize hash table */
2668     uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2669
2670     uzbl.net.soup_session = webkit_get_default_session();
2671     uzbl.state.keycmd = g_strdup("");
2672
2673     if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2674         fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2675     if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2676         fprintf(stderr, "uzbl: error hooking SIGINT\n");
2677     if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2678         fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2679
2680     uzbl.gui.sbar.progress_s = g_strdup("="); //TODO: move these to config.h
2681     uzbl.gui.sbar.progress_u = g_strdup("ยท");
2682     uzbl.gui.sbar.progress_w = 10;
2683
2684     /* HTML mode defaults*/
2685     uzbl.behave.html_buffer = g_string_new("");
2686     uzbl.behave.html_endmarker = g_strdup(".");
2687     uzbl.behave.html_timeout = 60;
2688     uzbl.behave.base_url = g_strdup("http://invalid");
2689
2690     /* default mode indicators */
2691     uzbl.behave.insert_indicator = g_strdup("I");
2692     uzbl.behave.cmd_indicator    = g_strdup("C");
2693
2694     uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2695     uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2696     uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2697     uzbl.info.arch         = ARCH;
2698     uzbl.info.commit       = COMMIT;
2699
2700     commands_hash ();
2701     make_var_to_name_hash();
2702
2703     create_browser();
2704 }
2705
2706 #ifndef UZBL_LIBRARY
2707 /** -- MAIN -- **/
2708 int
2709 main (int argc, char* argv[]) {
2710     initialize(argc, argv);
2711
2712     gtk_init (&argc, &argv);
2713
2714     uzbl.gui.scrolled_win = gtk_scrolled_window_new (NULL, NULL);
2715     //main_window_ref = g_object_ref(scrolled_window);
2716     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (uzbl.gui.scrolled_win),
2717         GTK_POLICY_NEVER, GTK_POLICY_NEVER); //todo: some sort of display of position/total length. like what emacs does
2718
2719     gtk_container_add (GTK_CONTAINER (uzbl.gui.scrolled_win),
2720         GTK_WIDGET (uzbl.gui.web_view));
2721
2722     uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2723
2724     create_mainbar();
2725
2726     /* initial packing */
2727     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2728     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2729
2730     if (uzbl.state.socket_id) {
2731         uzbl.gui.plug = create_plug ();
2732         gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2733         gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2734     } else {
2735         uzbl.gui.main_window = create_window ();
2736         gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2737         gtk_widget_show_all (uzbl.gui.main_window);
2738         uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2739     }
2740
2741     if(!uzbl.state.instance_name)
2742         uzbl.state.instance_name = itos((int)uzbl.xwin);
2743
2744     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2745
2746     if (uzbl.state.verbose) {
2747         printf("Uzbl start location: %s\n", argv[0]);
2748         if (uzbl.state.socket_id)
2749             printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2750         else
2751             printf("window_id %i\n",(int) uzbl.xwin);
2752         printf("pid %i\n", getpid ());
2753         printf("name: %s\n", uzbl.state.instance_name);
2754     }
2755
2756     uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2757     uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2758     uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2759     uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2760     gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2761
2762     if(uzbl.gui.geometry)
2763         cmd_set_geometry();
2764     else
2765         retrieve_geometry();
2766
2767     gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2768     if (argc > 1 && !uzbl.state.uri)
2769         uri_override = g_strdup(argv[1]);
2770     gboolean verbose_override = uzbl.state.verbose;
2771
2772     settings_init ();
2773     set_insert_mode(FALSE);
2774
2775     if (!uzbl.behave.show_status)
2776         gtk_widget_hide(uzbl.gui.mainbar);
2777     else
2778         update_title();
2779
2780     /* WebInspector */
2781     set_up_inspector();
2782
2783     if (verbose_override > uzbl.state.verbose)
2784         uzbl.state.verbose = verbose_override;
2785
2786     if (uri_override) {
2787         set_var_value("uri", uri_override);
2788         g_free(uri_override);
2789     } else if (uzbl.state.uri)
2790         cmd_load_uri(uzbl.gui.web_view, NULL);
2791
2792     gtk_main ();
2793     clean_up();
2794
2795     return EXIT_SUCCESS;
2796 }
2797 #endif
2798
2799 /* vi: set et ts=4: */