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