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