Added back in code to check if fifo exists, in case user wants to 'set fifo_dir ...
[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     if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1955         if (unlink(uzbl.comm.fifo_path) == -1)
1956             g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1957         g_free(uzbl.comm.fifo_path);
1958         uzbl.comm.fifo_path = NULL;
1959     }
1960
1961     GIOChannel *chan = NULL;
1962     GError *error = NULL;
1963     gchar *path = build_stream_name(FIFO, dir);
1964
1965     if (!file_exists(path)) {
1966         if (mkfifo (path, 0666) == 0) {
1967             // 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.
1968             chan = g_io_channel_new_file(path, "r+", &error);
1969             if (chan) {
1970                 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1971                     if (uzbl.state.verbose)
1972                         printf ("init_fifo: created successfully as %s\n", path);
1973                     uzbl.comm.fifo_path = path;
1974                     return dir;
1975                 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1976             } else g_warning ("init_fifo: can't open: %s\n", error->message);
1977         } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1978     } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1979
1980     /* if we got this far, there was an error; cleanup */
1981     if (error) g_error_free (error);
1982     g_free(dir);
1983     g_free(path);
1984     return NULL;
1985 }
1986
1987 gboolean
1988 control_stdin(GIOChannel *gio, GIOCondition condition) {
1989     (void) condition;
1990     gchar *ctl_line = NULL;
1991     GIOStatus ret;
1992
1993     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1994     if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1995         return FALSE;
1996
1997     parse_cmd_line(ctl_line, NULL);
1998     g_free(ctl_line);
1999
2000     return TRUE;
2001 }
2002
2003 void
2004 create_stdin () {
2005     GIOChannel *chan = NULL;
2006     GError *error = NULL;
2007
2008     chan = g_io_channel_unix_new(fileno(stdin));
2009     if (chan) {
2010         if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
2011             g_error ("Stdin: could not add watch\n");
2012         } else {
2013             if (uzbl.state.verbose)
2014                 printf ("Stdin: watch added successfully\n");
2015         }
2016     } else {
2017         g_error ("Stdin: Error while opening: %s\n", error->message);
2018     }
2019     if (error) g_error_free (error);
2020 }
2021
2022 gboolean
2023 control_socket(GIOChannel *chan) {
2024     struct sockaddr_un remote;
2025     unsigned int t = sizeof(remote);
2026     int clientsock;
2027     GIOChannel *clientchan;
2028
2029     clientsock = accept (g_io_channel_unix_get_fd(chan),
2030                          (struct sockaddr *) &remote, &t);
2031
2032     if ((clientchan = g_io_channel_unix_new(clientsock))) {
2033         g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2034                        (GIOFunc) control_client_socket, clientchan);
2035     }
2036
2037     return TRUE;
2038 }
2039
2040 gboolean
2041 control_client_socket(GIOChannel *clientchan) {
2042     char *ctl_line;
2043     GString *result = g_string_new("");
2044     GError *error = NULL;
2045     GIOStatus ret;
2046     gsize len;
2047
2048     ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2049     if (ret == G_IO_STATUS_ERROR) {
2050         g_warning ("Error reading: %s\n", error->message);
2051         g_io_channel_shutdown(clientchan, TRUE, &error);
2052         return FALSE;
2053     } else if (ret == G_IO_STATUS_EOF) {
2054         /* shutdown and remove channel watch from main loop */
2055         g_io_channel_shutdown(clientchan, TRUE, &error);
2056         return FALSE;
2057     }
2058
2059     if (ctl_line) {
2060         parse_cmd_line (ctl_line, result);
2061         g_string_append_c(result, '\n');
2062         ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2063                                         &len, &error);
2064         if (ret == G_IO_STATUS_ERROR) {
2065             g_warning ("Error writing: %s", error->message);
2066         }
2067         g_io_channel_flush(clientchan, &error);
2068     }
2069
2070     if (error) g_error_free (error);
2071     g_string_free(result, TRUE);
2072     g_free(ctl_line);
2073     return TRUE;
2074 }
2075
2076 gchar*
2077 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2078     if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2079         if (unlink(uzbl.comm.socket_path) == -1)
2080             g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2081         g_free(uzbl.comm.socket_path);
2082         uzbl.comm.socket_path = NULL;
2083     }
2084
2085     if (*dir == ' ') {
2086         g_free(dir);
2087         return NULL;
2088     }
2089
2090     GIOChannel *chan = NULL;
2091     int sock, len;
2092     struct sockaddr_un local;
2093     gchar *path = build_stream_name(SOCKET, dir);
2094
2095     sock = socket (AF_UNIX, SOCK_STREAM, 0);
2096
2097     local.sun_family = AF_UNIX;
2098     strcpy (local.sun_path, path);
2099     unlink (local.sun_path);
2100
2101     len = strlen (local.sun_path) + sizeof (local.sun_family);
2102     if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2103         if (uzbl.state.verbose)
2104             printf ("init_socket: opened in %s\n", path);
2105         listen (sock, 5);
2106
2107         if( (chan = g_io_channel_unix_new(sock)) ) {
2108             g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2109             uzbl.comm.socket_path = path;
2110             return dir;
2111         }
2112     } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2113
2114     /* if we got this far, there was an error; cleanup */
2115     g_free(path);
2116     g_free(dir);
2117     return NULL;
2118 }
2119
2120 /*
2121  NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2122  it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2123 */
2124 // this function may be called very early when the templates are not set (yet), hence the checks
2125 void
2126 update_title (void) {
2127     Behaviour *b = &uzbl.behave;
2128     gchar *parsed;
2129
2130     if (b->show_status) {
2131         if (b->title_format_short) {
2132             parsed = expand_template(b->title_format_short, FALSE);
2133             if (uzbl.gui.main_window)
2134                 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2135             g_free(parsed);
2136         }
2137         if (b->status_format) {
2138             parsed = expand_template(b->status_format, TRUE);
2139             gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2140             g_free(parsed);
2141         }
2142         if (b->status_background) {
2143             GdkColor color;
2144             gdk_color_parse (b->status_background, &color);
2145             //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)
2146             if (uzbl.gui.main_window)
2147                 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2148             else if (uzbl.gui.plug)
2149                 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2150         }
2151     } else {
2152         if (b->title_format_long) {
2153             parsed = expand_template(b->title_format_long, FALSE);
2154             if (uzbl.gui.main_window)
2155                 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2156             g_free(parsed);
2157         }
2158     }
2159 }
2160
2161 gboolean
2162 key_press_cb (GtkWidget* window, GdkEventKey* event)
2163 {
2164     //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2165
2166     (void) window;
2167
2168     if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2169         || 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)
2170         return FALSE;
2171
2172     /* turn off insert mode (if always_insert_mode is not used) */
2173     if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2174         uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
2175         update_title();
2176         return TRUE;
2177     }
2178
2179     if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2180         return FALSE;
2181
2182     if (event->keyval == GDK_Escape) {
2183         g_string_truncate(uzbl.state.keycmd, 0);
2184         update_title();
2185         dehilight(uzbl.gui.web_view, NULL, NULL);
2186         return TRUE;
2187     }
2188
2189     //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2190     if (event->keyval == GDK_Insert) {
2191         gchar * str;
2192         if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2193             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2194         } else {
2195             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2196         }
2197         if (str) {
2198             g_string_append (uzbl.state.keycmd, str);
2199             update_title ();
2200             g_free (str);
2201         }
2202         return TRUE;
2203     }
2204
2205     if (event->keyval == GDK_BackSpace)
2206         keycmd_bs(NULL, NULL, NULL);
2207
2208     gboolean key_ret = FALSE;
2209     if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2210         key_ret = TRUE;
2211     if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2212
2213     run_keycmd(key_ret);
2214     update_title();
2215     if (key_ret) return (!uzbl.behave.insert_mode);
2216     return TRUE;
2217 }
2218
2219 void
2220 run_keycmd(const gboolean key_ret) {
2221     /* run the keycmd immediately if it isn't incremental and doesn't take args */
2222     Action *act;
2223     if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2224         g_string_truncate(uzbl.state.keycmd, 0);
2225         parse_command(act->name, act->param, NULL);
2226         return;
2227     }
2228
2229     /* try if it's an incremental keycmd or one that takes args, and run it */
2230     GString* short_keys = g_string_new ("");
2231     GString* short_keys_inc = g_string_new ("");
2232     guint i;
2233     for (i=0; i<(uzbl.state.keycmd->len); i++) {
2234         g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2235         g_string_assign(short_keys_inc, short_keys->str);
2236         g_string_append_c(short_keys, '_');
2237         g_string_append_c(short_keys_inc, '*');
2238
2239         if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2240             /* run normal cmds only if return was pressed */
2241             exec_paramcmd(act, i);
2242             g_string_truncate(uzbl.state.keycmd, 0);
2243             break;
2244         } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2245             if (key_ret)  /* just quit the incremental command on return */
2246                 g_string_truncate(uzbl.state.keycmd, 0);
2247             else exec_paramcmd(act, i); /* otherwise execute the incremental */
2248             break;
2249         }
2250
2251         g_string_truncate(short_keys, short_keys->len - 1);
2252     }
2253     g_string_free (short_keys, TRUE);
2254     g_string_free (short_keys_inc, TRUE);
2255 }
2256
2257 void
2258 exec_paramcmd(const Action *act, const guint i) {
2259     GString *parampart = g_string_new (uzbl.state.keycmd->str);
2260     GString *actionname = g_string_new ("");
2261     GString *actionparam = g_string_new ("");
2262     g_string_erase (parampart, 0, i+1);
2263     if (act->name)
2264         g_string_printf (actionname, act->name, parampart->str);
2265     if (act->param)
2266         g_string_printf (actionparam, act->param, parampart->str);
2267     parse_command(actionname->str, actionparam->str, NULL);
2268     g_string_free(actionname, TRUE);
2269     g_string_free(actionparam, TRUE);
2270     g_string_free(parampart, TRUE);
2271 }
2272
2273
2274 GtkWidget*
2275 create_browser () {
2276     GUI *g = &uzbl.gui;
2277
2278     GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2279     //main_window_ref = g_object_ref(scrolled_window);
2280     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
2281
2282     g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2283     gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2284
2285     g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2286     g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2287     g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2288     g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2289     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2290     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2291     g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2292     g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2293     g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2294     g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2295     g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2296
2297     return scrolled_window;
2298 }
2299
2300 GtkWidget*
2301 create_mainbar () {
2302     GUI *g = &uzbl.gui;
2303
2304     g->mainbar = gtk_hbox_new (FALSE, 0);
2305
2306     /* keep a reference to the bar so we can re-pack it at runtime*/
2307     //sbar_ref = g_object_ref(g->mainbar);
2308
2309     g->mainbar_label = gtk_label_new ("");
2310     gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2311     gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2312     gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2313     gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2314     gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2315     g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2316     return g->mainbar;
2317 }
2318
2319 GtkWidget*
2320 create_window () {
2321     GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2322     gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2323     gtk_widget_set_name (window, "Uzbl browser");
2324     g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2325     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2326
2327     return window;
2328 }
2329
2330 GtkPlug*
2331 create_plug () {
2332     GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2333     g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2334     g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2335
2336     return plug;
2337 }
2338
2339
2340 gchar**
2341 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2342     /*
2343       If actname is one that calls an external command, this function will inject
2344       newargs in front of the user-provided args in that command line.  They will
2345       come become after the body of the script (in sh) or after the name of
2346       the command to execute (in spawn).
2347       i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2348       spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2349
2350       The return value consist of two strings: the action (sh, ...) and its args.
2351
2352       If act is not one that calls an external command, then the given action merely
2353       gets duplicated.
2354     */
2355     GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2356     /* Arrr! Here be memory leaks */
2357     gchar *actdup = g_strdup(actname);
2358     g_array_append_val(rets, actdup);
2359
2360     if ((g_strcmp0(actname, "spawn") == 0) ||
2361         (g_strcmp0(actname, "sh") == 0) ||
2362         (g_strcmp0(actname, "sync_spawn") == 0) ||
2363         (g_strcmp0(actname, "sync_sh") == 0)) {
2364         guint i;
2365         GString *a = g_string_new("");
2366         gchar **spawnparts = split_quoted(origargs, FALSE);
2367         g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2368         if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2369
2370         for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2371             if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2372
2373         g_array_append_val(rets, a->str);
2374         g_string_free(a, FALSE);
2375         g_strfreev(spawnparts);
2376     } else {
2377         gchar *origdup = g_strdup(origargs);
2378         g_array_append_val(rets, origdup);
2379     }
2380     return (gchar**)g_array_free(rets, FALSE);
2381 }
2382
2383 void
2384 run_handler (const gchar *act, const gchar *args) {
2385     /* Consider this code a temporary hack to make the handlers usable.
2386        In practice, all this splicing, injection, and reconstruction is
2387        inefficient, annoying and hard to manage.  Potential pitfalls arise
2388        when the handler specific args 1) are not quoted  (the handler
2389        callbacks should take care of this)  2) are quoted but interfere
2390        with the users' own quotation.  A more ideal solution is
2391        to refactor parse_command so that it doesn't just take a string
2392        and execute it; rather than that, we should have a function which
2393        returns the argument vector parsed from the string.  This vector
2394        could be modified (e.g. insert additional args into it) before
2395        passing it to the next function that actually executes it.  Though
2396        it still isn't perfect for chain actions..  will reconsider & re-
2397        factor when I have the time. -duc */
2398
2399     char **parts = g_strsplit(act, " ", 2);
2400     if (!parts) return;
2401     if (g_strcmp0(parts[0], "chain") == 0) {
2402         GString *newargs = g_string_new("");
2403         gchar **chainparts = split_quoted(parts[1], FALSE);
2404
2405         /* for every argument in the chain, inject the handler args
2406            and make sure the new parts are wrapped in quotes */
2407         gchar **cp = chainparts;
2408         gchar quot = '\'';
2409         gchar *quotless = NULL;
2410         gchar **spliced_quotless = NULL; // sigh -_-;
2411         gchar **inpart = NULL;
2412
2413         while (*cp) {
2414             if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2415                 quot = **cp;
2416                 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2417             } else quotless = g_strdup(*cp);
2418
2419             spliced_quotless = g_strsplit(quotless, " ", 2);
2420             inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2421             g_strfreev(spliced_quotless);
2422
2423             g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2424             g_free(quotless);
2425             g_strfreev(inpart);
2426             cp++;
2427         }
2428
2429         parse_command(parts[0], &(newargs->str[1]), NULL);
2430         g_string_free(newargs, TRUE);
2431         g_strfreev(chainparts);
2432
2433     } else {
2434         gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2435         parse_command(inparts[0], inparts[1], NULL);
2436         g_free(inparts[0]);
2437         g_free(inparts[1]);
2438     }
2439     g_strfreev(parts);
2440 }
2441
2442 void
2443 add_binding (const gchar *key, const gchar *act) {
2444     char **parts = g_strsplit(act, " ", 2);
2445     Action *action;
2446
2447     if (!parts)
2448         return;
2449
2450     //Debug:
2451     if (uzbl.state.verbose)
2452         printf ("Binding %-10s : %s\n", key, act);
2453     action = new_action(parts[0], parts[1]);
2454
2455     if (g_hash_table_remove (uzbl.bindings, key))
2456         g_warning ("Overwriting existing binding for \"%s\"", key);
2457     g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2458     g_strfreev(parts);
2459 }
2460
2461 gchar*
2462 get_xdg_var (XDG_Var xdg) {
2463     const gchar* actual_value = getenv (xdg.environmental);
2464     const gchar* home         = getenv ("HOME");
2465     gchar* return_value;
2466
2467     if (! actual_value || strcmp (actual_value, "") == 0) {
2468         if (xdg.default_value) {
2469             return_value = str_replace ("~", home, xdg.default_value);
2470         } else {
2471             return_value = NULL;
2472         }
2473     } else {
2474         return_value = str_replace("~", home, actual_value);
2475     }
2476
2477     return return_value;
2478 }
2479
2480 gchar*
2481 find_xdg_file (int xdg_type, char* filename) {
2482     /* xdg_type = 0 => config
2483        xdg_type = 1 => data
2484        xdg_type = 2 => cache*/
2485
2486     gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2487     gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2488     g_free (xdgv);
2489
2490     gchar* temporary_string;
2491     char*  saveptr;
2492     char*  buf;
2493
2494     if (! file_exists (temporary_file) && xdg_type != 2) {
2495         buf = get_xdg_var (XDG[3 + xdg_type]);
2496         temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2497         g_free(buf);
2498
2499         while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2500             g_free (temporary_file);
2501             temporary_file = g_strconcat (temporary_string, filename, NULL);
2502         }
2503     }
2504
2505     //g_free (temporary_string); - segfaults.
2506
2507     if (file_exists (temporary_file)) {
2508         return temporary_file;
2509     } else {
2510         return NULL;
2511     }
2512 }
2513 void
2514 settings_init () {
2515     State *s = &uzbl.state;
2516     Network *n = &uzbl.net;
2517     int i;
2518     for (i = 0; default_config[i].command != NULL; i++) {
2519         parse_cmd_line(default_config[i].command, NULL);
2520     }
2521
2522     if (g_strcmp0(s->config_file, "-") == 0) {
2523         s->config_file = NULL;
2524         create_stdin();
2525     }
2526
2527     else if (!s->config_file) {
2528         s->config_file = find_xdg_file (0, "/uzbl/config");
2529     }
2530
2531     if (s->config_file) {
2532         GArray* lines = read_file_by_line (s->config_file);
2533         int i = 0;
2534         gchar* line;
2535
2536         while ((line = g_array_index(lines, gchar*, i))) {
2537             parse_cmd_line (line, NULL);
2538             i ++;
2539             g_free (line);
2540         }
2541         g_array_free (lines, TRUE);
2542     } else {
2543         if (uzbl.state.verbose)
2544             printf ("No configuration file loaded.\n");
2545     }
2546
2547     g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2548 }
2549
2550 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2551     (void) session;
2552     (void) user_data;
2553     if (!uzbl.behave.cookie_handler)
2554          return;
2555
2556     soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2557     GString *s = g_string_new ("");
2558     SoupURI * soup_uri = soup_message_get_uri(msg);
2559     g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2560     run_handler(uzbl.behave.cookie_handler, s->str);
2561
2562     if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2563         char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2564         if ( p != NULL ) *p = '\0';
2565         soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2566     }
2567     if (uzbl.comm.sync_stdout)
2568         uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2569
2570     g_string_free(s, TRUE);
2571 }
2572
2573 void
2574 save_cookies (SoupMessage *msg, gpointer user_data){
2575     (void) user_data;
2576     GSList *ck;
2577     char *cookie;
2578     for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2579         cookie = soup_cookie_to_set_cookie_header(ck->data);
2580         SoupURI * soup_uri = soup_message_get_uri(msg);
2581         GString *s = g_string_new ("");
2582         g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2583         run_handler(uzbl.behave.cookie_handler, s->str);
2584         g_free (cookie);
2585         g_string_free(s, TRUE);
2586     }
2587     g_slist_free(ck);
2588 }
2589
2590 /* --- WEBINSPECTOR --- */
2591 void
2592 hide_window_cb(GtkWidget *widget, gpointer data) {
2593     (void) data;
2594
2595     gtk_widget_hide(widget);
2596 }
2597
2598 WebKitWebView*
2599 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2600     (void) data;
2601     (void) page;
2602     (void) web_inspector;
2603     GtkWidget* scrolled_window;
2604     GtkWidget* new_web_view;
2605     GUI *g = &uzbl.gui;
2606
2607     g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2608     g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2609             G_CALLBACK(hide_window_cb), NULL);
2610
2611     gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2612     gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2613     gtk_widget_show(g->inspector_window);
2614
2615     scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2616     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2617             GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2618     gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2619     gtk_widget_show(scrolled_window);
2620
2621     new_web_view = webkit_web_view_new();
2622     gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2623
2624     return WEBKIT_WEB_VIEW(new_web_view);
2625 }
2626
2627 gboolean
2628 inspector_show_window_cb (WebKitWebInspector* inspector){
2629     (void) inspector;
2630     gtk_widget_show(uzbl.gui.inspector_window);
2631     return TRUE;
2632 }
2633
2634 /* TODO: Add variables and code to make use of these functions */
2635 gboolean
2636 inspector_close_window_cb (WebKitWebInspector* inspector){
2637     (void) inspector;
2638     return TRUE;
2639 }
2640
2641 gboolean
2642 inspector_attach_window_cb (WebKitWebInspector* inspector){
2643     (void) inspector;
2644     return FALSE;
2645 }
2646
2647 gboolean
2648 inspector_detach_window_cb (WebKitWebInspector* inspector){
2649     (void) inspector;
2650     return FALSE;
2651 }
2652
2653 gboolean
2654 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2655     (void) inspector;
2656     return FALSE;
2657 }
2658
2659 gboolean
2660 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2661     (void) inspector;
2662     return FALSE;
2663 }
2664
2665 void
2666 set_up_inspector() {
2667     GUI *g = &uzbl.gui;
2668     WebKitWebSettings *settings = view_settings();
2669     g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2670
2671     uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2672     g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2673     g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2674     g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2675     g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2676     g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2677     g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2678
2679     g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2680 }
2681
2682 void
2683 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2684     (void) ud;
2685     uzbl_cmdprop *c = v;
2686
2687     if(!c->dump)
2688         return;
2689
2690     if(c->type == TYPE_STR)
2691         printf("set %s = %s\n", (char *)k, *c->ptr ? (char *)*c->ptr : " ");
2692     else if(c->type == TYPE_INT)
2693         printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2694     else if(c->type == TYPE_FLOAT)
2695         printf("set %s = %f\n", (char *)k, *(float *)c->ptr);
2696 }
2697
2698 void
2699 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2700     (void) ud;
2701     Action *a = v;
2702
2703     printf("bind %s = %s %s\n", (char *)k ,
2704             (char *)a->name, a->param?(char *)a->param:"");
2705 }
2706
2707 void
2708 dump_config() {
2709     g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2710     g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2711 }
2712
2713 #ifndef UZBL_LIBRARY
2714 /** -- MAIN -- **/
2715 int
2716 main (int argc, char* argv[]) {
2717     gtk_init (&argc, &argv);
2718     if (!g_thread_supported ())
2719         g_thread_init (NULL);
2720     uzbl.state.executable_path = g_strdup(argv[0]);
2721     uzbl.state.selected_url = NULL;
2722     uzbl.state.searchtx = NULL;
2723
2724     GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2725     g_option_context_add_main_entries (context, entries, NULL);
2726     g_option_context_add_group (context, gtk_get_option_group (TRUE));
2727     g_option_context_parse (context, &argc, &argv, NULL);
2728     g_option_context_free(context);
2729
2730     if (uzbl.behave.print_version) {
2731         printf("Commit: %s\n", COMMIT);
2732         exit(0);
2733     }
2734
2735     gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2736     if (argc > 1 && !uzbl.state.uri)
2737         uri_override = g_strdup(argv[1]);
2738     gboolean verbose_override = uzbl.state.verbose;
2739
2740     /* initialize hash table */
2741     uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2742
2743     uzbl.net.soup_session = webkit_get_default_session();
2744     uzbl.state.keycmd = g_string_new("");
2745
2746     if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2747         fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2748     if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2749         fprintf(stderr, "uzbl: error hooking SIGINT\n");
2750     if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2751         fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2752
2753
2754     if(uname(&uzbl.state.unameinfo) == -1)
2755         g_printerr("Can't retrieve unameinfo.  Your useragent might appear wrong.\n");
2756
2757     uzbl.gui.sbar.progress_s = g_strdup("=");
2758     uzbl.gui.sbar.progress_u = g_strdup("·");
2759     uzbl.gui.sbar.progress_w = 10;
2760
2761     /* HTML mode defaults*/
2762     uzbl.behave.html_buffer = g_string_new("");
2763     uzbl.behave.html_endmarker = g_strdup(".");
2764     uzbl.behave.html_timeout = 60;
2765     uzbl.behave.base_url = g_strdup("http://invalid");
2766
2767     /* default mode indicators */
2768     uzbl.behave.insert_indicator = g_strdup("I");
2769     uzbl.behave.cmd_indicator    = g_strdup("C");
2770
2771     setup_scanner();
2772     commands_hash ();
2773     make_var_to_name_hash();
2774
2775     uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2776
2777     uzbl.gui.scrolled_win = create_browser();
2778     create_mainbar();
2779
2780     /* initial packing */
2781     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2782     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2783
2784     if (uzbl.state.socket_id) {
2785         uzbl.gui.plug = create_plug ();
2786         gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2787         gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2788     } else {
2789         uzbl.gui.main_window = create_window ();
2790         gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2791         gtk_widget_show_all (uzbl.gui.main_window);
2792         uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2793     }
2794
2795     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2796
2797     if (uzbl.state.verbose) {
2798         printf("Uzbl start location: %s\n", argv[0]);
2799         if (uzbl.state.socket_id)
2800             printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2801         else
2802             printf("window_id %i\n",(int) uzbl.xwin);
2803         printf("pid %i\n", getpid ());
2804         printf("name: %s\n", uzbl.state.instance_name);
2805     }
2806
2807     uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2808     uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2809     uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2810     uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2811     gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2812
2813     settings_init ();
2814
2815     if (!uzbl.behave.show_status)
2816         gtk_widget_hide(uzbl.gui.mainbar);
2817     else
2818         update_title();
2819
2820     /* WebInspector */
2821     set_up_inspector();
2822
2823     if (verbose_override > uzbl.state.verbose)
2824         uzbl.state.verbose = verbose_override;
2825
2826     if (uri_override) {
2827         set_var_value("uri", uri_override);
2828         g_free(uri_override);
2829     } else if (uzbl.state.uri)
2830         cmd_load_uri(uzbl.gui.web_view, NULL);
2831
2832     gtk_main ();
2833     clean_up();
2834
2835     return EXIT_SUCCESS;
2836 }
2837 #endif
2838
2839 /* vi: set et ts=4: */