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