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