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