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