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