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