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