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