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