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