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