set current geometry on configure-event
[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 static Uzbl uzbl;
61
62
63
64 /* commandline arguments (set initial values for the state variables) */
65 static const 
66 GOptionEntry entries[] =
67 {
68     { "uri",      'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
69         "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
70     { "verbose",  'v', 0, G_OPTION_ARG_NONE,   &uzbl.state.verbose,
71         "Whether to print all messages or just errors.", NULL },
72     { "name",     'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name, 
73         "Name of the current instance (defaults to Xorg window id)", "NAME" },
74     { "config",   'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,   
75         "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
76     { "socket",   's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,   
77         "Socket ID", "SOCKET" },
78     { "geometry", 'g', 0, G_OPTION_ARG_STRING, &uzbl.gui.geometry, 
79         "Set window geometry (format: WIDTHxHEIGHT+-X+-Y)", "GEOMETRY" },
80     { NULL,      0, 0, 0, NULL, NULL, NULL }
81 };
82
83 /* associate command names to their properties */
84 typedef const struct {
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     { "geometry",            PTR(uzbl.gui.geometry,               STR,  1,   cmd_set_geometry)},
104     { "verbose",             PTR(uzbl.state.verbose,              INT,  1,   NULL)},
105     { "mode",                PTR(uzbl.behave.mode,                INT,  0,   NULL)},
106     { "inject_html",         PTR(uzbl.behave.inject_html,         STR,  0,   cmd_inject_html)},
107     { "base_url",            PTR(uzbl.behave.base_url,            STR,  1,   NULL)},
108     { "html_endmarker",      PTR(uzbl.behave.html_endmarker,      STR,  1,   NULL)},
109     { "html_mode_timeout",   PTR(uzbl.behave.html_timeout,        INT,  1,   NULL)},
110     { "status_message",      PTR(uzbl.gui.sbar.msg,               STR,  1,   update_title)},
111     { "show_status",         PTR(uzbl.behave.show_status,         INT,  1,   cmd_set_status)},
112     { "status_top",          PTR(uzbl.behave.status_top,          INT,  1,   move_statusbar)},
113     { "status_format",       PTR(uzbl.behave.status_format,       STR,  1,   update_title)},
114     { "status_pbar_done",    PTR(uzbl.gui.sbar.progress_s,        STR,  1,   update_title)},
115     { "status_pbar_pending", PTR(uzbl.gui.sbar.progress_u,        STR,  1,   update_title)},
116     { "status_pbar_width",   PTR(uzbl.gui.sbar.progress_w,        INT,  1,   update_title)},
117     { "status_background",   PTR(uzbl.behave.status_background,   STR,  1,   update_title)},
118     { "insert_indicator",    PTR(uzbl.behave.insert_indicator,    STR,  1,   update_title)},
119     { "command_indicator",   PTR(uzbl.behave.cmd_indicator,       STR,  1,   update_title)},
120     { "title_format_long",   PTR(uzbl.behave.title_format_long,   STR,  1,   update_title)},
121     { "title_format_short",  PTR(uzbl.behave.title_format_short,  STR,  1,   update_title)},
122     { "icon",                PTR(uzbl.gui.icon,                   STR,  1,   set_icon)},
123     { "insert_mode",         PTR(uzbl.behave.insert_mode,         INT,  1,   NULL)},
124     { "always_insert_mode",  PTR(uzbl.behave.always_insert_mode,  INT,  1,   cmd_always_insert_mode)},
125     { "reset_command_mode",  PTR(uzbl.behave.reset_command_mode,  INT,  1,   NULL)},
126     { "modkey",              PTR(uzbl.behave.modkey,              STR,  1,   cmd_modkey)},
127     { "load_finish_handler", PTR(uzbl.behave.load_finish_handler, STR,  1,   NULL)},
128     { "load_start_handler",  PTR(uzbl.behave.load_start_handler,  STR,  1,   NULL)},
129     { "load_commit_handler", PTR(uzbl.behave.load_commit_handler, STR,  1,   NULL)},
130     { "history_handler",     PTR(uzbl.behave.history_handler,     STR,  1,   NULL)},
131     { "download_handler",    PTR(uzbl.behave.download_handler,    STR,  1,   NULL)},
132     { "cookie_handler",      PTR(uzbl.behave.cookie_handler,      STR,  1,   cmd_cookie_handler)},
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 static 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
198 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS};
199 static guint
200 get_exp_type(gchar *s) {
201     /* variables */
202     if(*(s+1) == '(')
203         return EXP_EXPR;
204     else if(*(s+1) == '{')
205         return EXP_BRACED_VAR;
206     else if(*(s+1) == '<')
207         return EXP_JS;
208     else
209         return EXP_SIMPLE_VAR;
210
211 return EXP_ERR;
212 }
213
214 /* 
215  * recurse == 1: don't expand '@(command)@'
216  * recurse == 2: don't expand '@<java script>@'
217  */
218 static gchar *
219 expand(char *s, guint recurse) {
220     uzbl_cmdprop *c;
221     guint etype;
222     char upto = ' ';
223     char *end_simple_var = "^°!\"§$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]¹²³¼½";
224     char str_end[2];
225     char ret[4096];
226     char *vend;
227     GError *err = NULL;
228     gchar *cmd_stdout = NULL;
229     gchar *mycmd = NULL;
230     GString *buf = g_string_new("");
231     GString *js_ret = g_string_new("");
232
233     while(*s) {
234         switch(*s) {
235             case '\\':
236                 g_string_append_c(buf, *++s);
237                 s++;
238                 break;
239
240             case '@':
241                 etype = get_exp_type(s);
242                 s++;
243
244                 switch(etype) {
245                     case EXP_SIMPLE_VAR:
246                         if( (vend = strpbrk(s, end_simple_var)) ||
247                             (vend = strchr(s, '\0')) ) {
248                             strncpy(ret, s, vend-s);
249                             ret[vend-s] = '\0';
250                         }
251                         break;
252                     case EXP_BRACED_VAR:
253                         s++; upto = '}';
254                         if( (vend = strchr(s, upto)) ||
255                             (vend = strchr(s, '\0')) ) {
256                             strncpy(ret, s, vend-s);
257                             ret[vend-s] = '\0';
258                         }
259                         break;
260                     case EXP_EXPR:
261                         s++;
262                         strcpy(str_end, ")@");
263                         str_end[2] = '\0';
264                         if( (vend = strstr(s, str_end)) ||
265                             (vend = strchr(s, '\0')) ) {
266                             strncpy(ret, s, vend-s);
267                             ret[vend-s] = '\0';
268                         }
269                         break;
270                     case EXP_JS: 
271                         s++;
272                         strcpy(str_end, ">@");
273                         str_end[2] = '\0';
274                         if( (vend = strstr(s, str_end)) ||
275                             (vend = strchr(s, '\0')) ) {
276                             strncpy(ret, s, vend-s);
277                             ret[vend-s] = '\0';
278                         }
279                         break;
280                 }
281
282                 if(etype == EXP_SIMPLE_VAR || 
283                    etype == EXP_BRACED_VAR) {
284                     if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
285                         if(c->type == TYPE_STR)
286                             g_string_append(buf, (gchar *)*c->ptr);
287                         else if(c->type == TYPE_INT) {
288                             char *b = itos((int)*c->ptr);
289                             g_string_append(buf, b);
290                             g_free(b);
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 static gchar*
348 strfree(gchar *str) { g_free(str); return NULL; }  // for freeing & setting to null in one go
349
350 static gchar*
351 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
352
353 static 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 static 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 static
392 gchar* 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 static 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 static 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 }
443
444 /* used for html_mode_timeout 
445  * be sure to extend this function to use
446  * more timers if needed in other places
447 */
448 static void
449 set_timeout(int seconds) {
450     struct itimerval t;
451     memset(&t, 0, sizeof t);
452
453     t.it_value.tv_sec =  seconds;
454     t.it_value.tv_usec = 0;
455     setitimer(ITIMER_REAL, &t, NULL);
456 }
457
458 /* --- SIGNAL HANDLER --- */
459
460 static void
461 catch_sigterm(int s) {
462     (void) s;
463     clean_up();
464 }
465
466 static void
467 catch_sigint(int s) {
468     (void) s;
469     clean_up();
470     exit(EXIT_SUCCESS);
471 }
472
473 static void
474 catch_alrm(int s) {
475     (void) s;
476
477     set_var_value("mode", "0");
478     render_html();
479 }
480
481
482 /* --- CALLBACKS --- */
483
484 static gboolean
485 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
486     (void) web_view;
487     (void) frame;
488     (void) navigation_action;
489     (void) policy_decision;
490     (void) user_data;
491     const gchar* uri = webkit_network_request_get_uri (request);
492     if (uzbl.state.verbose)
493         printf("New window requested -> %s \n", uri);
494     new_window_load_uri(uri);
495     return (FALSE);
496 }
497
498 static gboolean
499 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type,  WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
500     (void) frame;
501     (void) request;
502     (void) user_data;
503
504     /* If we can display it, let's display it... */
505     if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
506         webkit_web_policy_decision_use (policy_decision);
507         return TRUE;
508     }
509
510     /* ...everything we can't displayed is downloaded */
511     webkit_web_policy_decision_download (policy_decision);
512     return TRUE;
513 }
514
515 WebKitWebView*
516 create_web_view_cb (WebKitWebView  *web_view, WebKitWebFrame *frame, gpointer user_data) {
517     (void) web_view;
518     (void) frame;
519     (void) user_data;
520     if (uzbl.state.selected_url != NULL) {
521         if (uzbl.state.verbose)
522             printf("\nNew web view -> %s\n",uzbl.state.selected_url);
523         new_window_load_uri(uzbl.state.selected_url);
524     } else {
525         if (uzbl.state.verbose)
526             printf("New web view -> %s\n","Nothing to open, exiting");
527     }
528     return (NULL);
529 }
530
531 static gboolean
532 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
533     (void) web_view;
534     (void) user_data;
535     if (uzbl.behave.download_handler) {
536         const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
537         if (uzbl.state.verbose)
538             printf("Download -> %s\n",uri);
539         /* if urls not escaped, we may have to escape and quote uri before this call */
540         run_handler(uzbl.behave.download_handler, uri);
541     }
542     return (FALSE);
543 }
544
545 /* scroll a bar in a given direction */
546 static void
547 scroll (GtkAdjustment* bar, GArray *argv) {
548     gchar *end;
549     gdouble max_value;
550
551     gdouble page_size = gtk_adjustment_get_page_size(bar);
552     gdouble value = gtk_adjustment_get_value(bar);
553     gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
554
555     if (*end == '%')
556         value += page_size * amount * 0.01;
557     else
558         value += amount;
559
560     max_value = gtk_adjustment_get_upper(bar) - page_size;
561
562     if (value > max_value)
563         value = max_value; /* don't scroll past the end of the page */
564
565     gtk_adjustment_set_value (bar, value);
566 }
567
568 static void
569 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
570     (void) page; (void) argv; (void) result;
571     gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
572 }
573
574 static void
575 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
576     (void) page; (void) argv; (void) result;
577     gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
578                               gtk_adjustment_get_page_size(uzbl.gui.bar_v));
579 }
580
581 static void
582 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
583     (void) page; (void) result;
584     scroll(uzbl.gui.bar_v, argv);
585 }
586
587 static void
588 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
589     (void) page; (void) result;
590     scroll(uzbl.gui.bar_h, argv);
591 }
592
593 static void
594 cmd_set_geometry() {
595     if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
596         if(uzbl.state.verbose)
597             printf("Error in geometry string: %s\n", uzbl.gui.geometry);
598     }
599 }
600
601 static void
602 cmd_set_status() {
603     if (!uzbl.behave.show_status) {
604         gtk_widget_hide(uzbl.gui.mainbar);
605     } else {
606         gtk_widget_show(uzbl.gui.mainbar);
607     }
608     update_title();
609 }
610
611 static void
612 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
613     (void)page;
614     (void)argv;
615     (void)result;
616
617     webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
618 }
619
620 static void
621 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
622     (void)page;
623     (void)argv;
624     (void)result;
625
626     if (uzbl.behave.show_status) {
627         gtk_widget_hide(uzbl.gui.mainbar);
628     } else {
629         gtk_widget_show(uzbl.gui.mainbar);
630     }
631     uzbl.behave.show_status = !uzbl.behave.show_status;
632     update_title();
633 }
634
635 static void
636 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
637     (void) page;
638     (void) title;
639     (void) data;
640     //Set selected_url state variable
641     g_free(uzbl.state.selected_url);
642     uzbl.state.selected_url = NULL;
643     if (link) {
644         uzbl.state.selected_url = g_strdup(link);
645     }
646     update_title();
647 }
648
649 static void
650 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
651     (void) web_view;
652     (void) param_spec;
653     const gchar *title = webkit_web_view_get_title(web_view);
654     if (uzbl.gui.main_title)
655         g_free (uzbl.gui.main_title);
656     uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
657     update_title();
658 }
659
660 static void
661 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
662     (void) page;
663     (void) data;
664     uzbl.gui.sbar.load_progress = progress;
665     update_title();
666 }
667
668 static void
669 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
670     (void) page;
671     (void) frame;
672     (void) data;
673     if (uzbl.behave.load_finish_handler)
674         run_handler(uzbl.behave.load_finish_handler, "");
675 }
676
677 static void
678 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
679     (void) page;
680     (void) frame;
681     (void) data;
682     uzbl.gui.sbar.load_progress = 0;
683     g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
684     if (uzbl.behave.load_start_handler)
685         run_handler(uzbl.behave.load_start_handler, "");
686 }
687
688 static void
689 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
690     (void) page;
691     (void) data;
692     g_free (uzbl.state.uri);
693     GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
694     uzbl.state.uri = g_string_free (newuri, FALSE);
695     if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
696         uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
697         update_title();
698     }
699     if (uzbl.behave.load_commit_handler)
700         run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
701 }
702
703 static void
704 destroy_cb (GtkWidget* widget, gpointer data) {
705     (void) widget;
706     (void) data;
707     gtk_main_quit ();
708 }
709
710 static void
711 log_history_cb () {
712    if (uzbl.behave.history_handler) {
713        time_t rawtime;
714        struct tm * timeinfo;
715        char date [80];
716        time ( &rawtime );
717        timeinfo = localtime ( &rawtime );
718        strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
719        run_handler(uzbl.behave.history_handler, date);
720    }
721 }
722
723
724 /* VIEW funcs (little webkit wrappers) */
725 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
726 VIEWFUNC(reload)
727 VIEWFUNC(reload_bypass_cache)
728 VIEWFUNC(stop_loading)
729 VIEWFUNC(zoom_in)
730 VIEWFUNC(zoom_out)
731 VIEWFUNC(go_back)
732 VIEWFUNC(go_forward)
733 #undef VIEWFUNC
734
735 /* -- command to callback/function map for things we cannot attach to any signals */
736 static struct {char *key; CommandInfo value;} cmdlist[] =
737 {   /* key                   function      no_split      */
738     { "back",               {view_go_back, 0}              },
739     { "forward",            {view_go_forward, 0}           },
740     { "scroll_vert",        {scroll_vert, 0}               },
741     { "scroll_horz",        {scroll_horz, 0}               },
742     { "scroll_begin",       {scroll_begin, 0}              },
743     { "scroll_end",         {scroll_end, 0}                },
744     { "reload",             {view_reload, 0},              },
745     { "reload_ign_cache",   {view_reload_bypass_cache, 0}  },
746     { "stop",               {view_stop_loading, 0},        },
747     { "zoom_in",            {view_zoom_in, 0},             }, //Can crash (when max zoom reached?).
748     { "zoom_out",           {view_zoom_out, 0},            },
749     { "toggle_zoom_type",   {toggle_zoom_type, 0},         },
750     { "uri",                {load_uri, TRUE}               },
751     { "js",                 {run_js, TRUE}                 },
752     { "script",             {run_external_js, 0}           },
753     { "toggle_status",      {toggle_status_cb, 0}          },
754     { "spawn",              {spawn, 0}                     },
755     { "sync_spawn",         {spawn_sync, 0}                }, // needed for cookie handler
756     { "sh",                 {spawn_sh, 0}                  },
757     { "sync_sh",            {spawn_sh_sync, 0}             }, // needed for cookie handler
758     { "exit",               {close_uzbl, 0}                },
759     { "search",             {search_forward_text, TRUE}    },
760     { "search_reverse",     {search_reverse_text, TRUE}    },
761     { "dehilight",          {dehilight, 0}                 },
762     { "toggle_insert_mode", {toggle_insert_mode, 0}        },
763     { "set",                {set_var, TRUE}                },
764   //{ "get",                {get_var, TRUE}                },
765     { "bind",               {act_bind, TRUE}               },
766     { "dump_config",        {act_dump_config, 0}           },
767     { "keycmd",             {keycmd, TRUE}                 },
768     { "keycmd_nl",          {keycmd_nl, TRUE}              },
769     { "keycmd_bs",          {keycmd_bs, 0}                 },
770     { "chain",              {chain, 0}                     },
771     { "print",              {print, TRUE}                  }
772 };
773
774 static void
775 commands_hash(void)
776 {
777     unsigned int i;
778     uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
779
780     for (i = 0; i < LENGTH(cmdlist); i++)
781         g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
782 }
783
784 /* -- CORE FUNCTIONS -- */
785
786 void
787 free_action(gpointer act) {
788     Action *action = (Action*)act;
789     g_free(action->name);
790     if (action->param)
791         g_free(action->param);
792     g_free(action);
793 }
794
795 Action*
796 new_action(const gchar *name, const gchar *param) {
797     Action *action = g_new(Action, 1);
798
799     action->name = g_strdup(name);
800     if (param)
801         action->param = g_strdup(param);
802     else
803         action->param = NULL;
804
805     return action;
806 }
807
808 static bool
809 file_exists (const char * filename) {
810     return (access(filename, F_OK) == 0);
811 }
812
813 static void
814 set_var(WebKitWebView *page, GArray *argv, GString *result) {
815     (void) page; (void) result;
816     gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
817     if (split[0] != NULL) {
818         gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
819         set_var_value(g_strstrip(split[0]), value);
820         g_free(value);
821     }
822     g_strfreev(split);
823 }
824
825 static void
826 print(WebKitWebView *page, GArray *argv, GString *result) {
827     (void) page; (void) result;
828     gchar* buf;
829
830     buf = expand(argv_idx(argv, 0), 0);
831     g_string_assign(result, buf);
832     g_free(buf);
833 }
834
835 static void
836 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
837     (void) page; (void) result;
838     gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
839     gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
840     add_binding(g_strstrip(split[0]), value);
841     g_free(value);
842     g_strfreev(split);
843 }
844
845
846 static void
847 act_dump_config() {
848     dump_config();
849 }
850
851 static void
852 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
853     (void) page; (void) result;
854
855     if (argv_idx(argv, 0)) {
856         if (strcmp (argv_idx(argv, 0), "0") == 0) {
857             uzbl.behave.insert_mode = FALSE;
858         } else {
859             uzbl.behave.insert_mode = TRUE;
860         }
861     } else {
862         uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
863     }
864
865     update_title();
866 }
867
868 static void
869 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
870     (void) result;
871
872     if (argv_idx(argv, 0)) {
873         GString* newuri = g_string_new (argv_idx(argv, 0));
874         if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
875             run_js(web_view, argv, NULL);
876             return;
877         }
878         if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
879             g_string_prepend (newuri, "http://");
880         /* if we do handle cookies, ask our handler for them */
881         webkit_web_view_load_uri (web_view, newuri->str);
882         g_string_free (newuri, TRUE);
883     }
884 }
885
886
887 /* Javascript*/
888
889 static JSValueRef
890 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
891                 size_t argumentCount, const JSValueRef arguments[],
892                 JSValueRef* exception) {
893     (void) function;
894     (void) thisObject;
895     (void) exception;
896     
897     JSStringRef js_result_string;
898     GString *result = g_string_new("");
899
900     if (argumentCount >= 1) {
901         JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
902         size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
903         char ctl_line[arg_size];
904         JSStringGetUTF8CString(arg, ctl_line, arg_size);
905
906         parse_cmd_line(ctl_line, result);
907
908         JSStringRelease(arg);
909     }
910     js_result_string = JSStringCreateWithUTF8CString(result->str);
911
912     g_string_free(result, TRUE);
913
914     return JSValueMakeString(ctx, js_result_string);
915 }
916
917 static JSStaticFunction js_static_functions[] = {
918     {"run", js_run_command, kJSPropertyAttributeNone},
919 };
920
921 static void
922 js_init() {
923     /* This function creates the class and its definition, only once */
924     if (!uzbl.js.initialized) {
925         /* it would be pretty cool to make this dynamic */
926         uzbl.js.classdef = kJSClassDefinitionEmpty;
927         uzbl.js.classdef.staticFunctions = js_static_functions;
928
929         uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
930     }
931 }
932
933
934 static void 
935 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
936     WebKitWebFrame *frame;
937     JSGlobalContextRef context;
938     JSObjectRef globalobject;
939     JSStringRef var_name;
940
941     JSStringRef js_script;
942     JSValueRef js_result;
943     JSStringRef js_result_string;
944     size_t js_result_size;
945     
946     js_init();
947
948     frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
949     context = webkit_web_frame_get_global_context(frame);
950     globalobject = JSContextGetGlobalObject(context);
951     
952     /* uzbl javascript namespace */
953     var_name = JSStringCreateWithUTF8CString("Uzbl");
954     JSObjectSetProperty(context, globalobject, var_name,
955                         JSObjectMake(context, uzbl.js.classref, NULL),  
956                         kJSClassAttributeNone, NULL);
957     
958     /* evaluate the script and get return value*/ 
959     js_script = JSStringCreateWithUTF8CString(script);
960     js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
961     if (js_result && !JSValueIsUndefined(context, js_result)) {
962         js_result_string = JSValueToStringCopy(context, js_result, NULL);
963         js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
964
965         if (js_result_size) {
966             char js_result_utf8[js_result_size];
967             JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
968             g_string_assign(result, js_result_utf8);
969         }
970
971         JSStringRelease(js_result_string);
972     }
973
974     /* cleanup */
975     JSObjectDeleteProperty(context, globalobject, var_name, NULL);
976
977     JSStringRelease(var_name);
978     JSStringRelease(js_script);
979 }
980
981 static void
982 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
983
984     if (argv_idx(argv, 0))
985         eval_js(web_view, argv_idx(argv, 0), result);
986 }
987
988 static void
989 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
990     (void) result;
991     if (argv_idx(argv, 0)) {
992         GArray* lines = read_file_by_line (argv_idx (argv, 0));
993         gchar*  js = NULL;
994         int i = 0;
995         gchar* line;
996
997         while ((line = g_array_index(lines, gchar*, i))) {
998             if (js == NULL) {
999                 js = g_strdup (line);
1000             } else {
1001                 gchar* newjs = g_strconcat (js, line, NULL);
1002                 js = newjs;
1003             }
1004             i ++;
1005             g_free (line);
1006         }
1007         
1008         if (uzbl.state.verbose)
1009             printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1010
1011         if (argv_idx (argv, 1)) {
1012             gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1013             g_free (js);
1014             js = newjs;
1015         }
1016         eval_js (web_view, js, result);
1017         g_free (js);
1018         g_array_free (lines, TRUE);
1019     }
1020 }
1021
1022 static void
1023 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1024     if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1025         if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1026             webkit_web_view_unmark_text_matches (page);
1027             webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1028             uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1029         }
1030     }
1031     
1032     if (uzbl.state.searchtx) {
1033         if (uzbl.state.verbose)
1034             printf ("Searching: %s\n", uzbl.state.searchtx);
1035         webkit_web_view_set_highlight_text_matches (page, TRUE);
1036         webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1037     }
1038 }
1039
1040 static void
1041 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1042     (void) result;
1043     search_text(page, argv, TRUE);
1044 }
1045
1046 static void
1047 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1048     (void) result;
1049     search_text(page, argv, FALSE);
1050 }
1051
1052 static void
1053 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1054     (void) argv; (void) result;
1055     webkit_web_view_set_highlight_text_matches (page, FALSE);
1056 }
1057
1058
1059 static void
1060 new_window_load_uri (const gchar * uri) {
1061     GString* to_execute = g_string_new ("");
1062     g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1063     int i;
1064     for (i = 0; entries[i].long_name != NULL; i++) {
1065         if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1066             gchar** str = (gchar**)entries[i].arg_data;
1067             if (*str!=NULL) {
1068                 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1069             }
1070         }
1071     }
1072     if (uzbl.state.verbose)
1073         printf("\n%s\n", to_execute->str);
1074     g_spawn_command_line_async (to_execute->str, NULL);
1075     g_string_free (to_execute, TRUE);
1076 }
1077
1078 static void
1079 chain (WebKitWebView *page, GArray *argv, GString *result) {
1080     (void) page; (void) result;
1081     gchar *a = NULL;
1082     gchar **parts = NULL;
1083     guint i = 0;    
1084     while ((a = argv_idx(argv, i++))) {
1085         parts = g_strsplit (a, " ", 2);
1086         parse_command(parts[0], parts[1], result);
1087         g_strfreev (parts);
1088     }
1089 }
1090
1091 static void
1092 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1093     (void)page;
1094     (void)argv;
1095     (void)result;
1096     g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1097     run_keycmd(FALSE);
1098     update_title();
1099 }
1100
1101 static void
1102 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1103     (void)page;
1104     (void)argv;
1105     (void)result;
1106     g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1107     run_keycmd(TRUE);
1108     update_title();
1109 }
1110
1111 static void
1112 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1113     gchar *prev;
1114     (void)page;
1115     (void)argv;
1116     (void)result;
1117     prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
1118     if (prev)
1119       g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1120     update_title();
1121 }
1122
1123 static void
1124 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1125     (void)page;
1126     (void)argv;
1127     (void)result;
1128     gtk_main_quit ();
1129 }
1130
1131 /* --Statusbar functions-- */
1132 static char*
1133 build_progressbar_ascii(int percent) {
1134    int width=uzbl.gui.sbar.progress_w;
1135    int i;
1136    double l;
1137    GString *bar = g_string_new("");
1138
1139    l = (double)percent*((double)width/100.);
1140    l = (int)(l+.5)>=(int)l ? l+.5 : l;
1141
1142    for(i=0; i<(int)l; i++)
1143        g_string_append(bar, uzbl.gui.sbar.progress_s);
1144
1145    for(; i<width; i++)
1146        g_string_append(bar, uzbl.gui.sbar.progress_u);
1147
1148    return g_string_free(bar, FALSE);
1149 }
1150
1151 static void
1152 setup_scanner() {
1153      const GScannerConfig scan_config = {
1154              (
1155               "\t\r\n"
1156              )            /* cset_skip_characters */,
1157              (
1158               G_CSET_a_2_z
1159               "_#"
1160               G_CSET_A_2_Z
1161              )            /* cset_identifier_first */,
1162              (
1163               G_CSET_a_2_z
1164               "_0123456789"
1165               G_CSET_A_2_Z
1166               G_CSET_LATINS
1167               G_CSET_LATINC
1168              )            /* cset_identifier_nth */,
1169              ( "" )    /* cpair_comment_single */,
1170
1171              TRUE         /* case_sensitive */,
1172
1173              FALSE        /* skip_comment_multi */,
1174              FALSE        /* skip_comment_single */,
1175              FALSE        /* scan_comment_multi */,
1176              TRUE         /* scan_identifier */,
1177              TRUE         /* scan_identifier_1char */,
1178              FALSE        /* scan_identifier_NULL */,
1179              TRUE         /* scan_symbols */,
1180              FALSE        /* scan_binary */,
1181              FALSE        /* scan_octal */,
1182              FALSE        /* scan_float */,
1183              FALSE        /* scan_hex */,
1184              FALSE        /* scan_hex_dollar */,
1185              FALSE        /* scan_string_sq */,
1186              FALSE        /* scan_string_dq */,
1187              TRUE         /* numbers_2_int */,
1188              FALSE        /* int_2_float */,
1189              FALSE        /* identifier_2_string */,
1190              FALSE        /* char_2_token */,
1191              FALSE        /* symbol_2_token */,
1192              TRUE         /* scope_0_fallback */,
1193              FALSE,
1194              TRUE
1195      };
1196
1197      uzbl.scan = g_scanner_new(&scan_config);
1198      while(symp->symbol_name) {
1199          g_scanner_scope_add_symbol(uzbl.scan, 0,
1200                          symp->symbol_name,
1201                          GINT_TO_POINTER(symp->symbol_token));
1202          symp++;
1203      }
1204 }
1205
1206 static gchar *
1207 expand_template(const char *template, gboolean escape_markup) {
1208      if(!template) return NULL;
1209
1210      GTokenType token = G_TOKEN_NONE;
1211      GString *ret = g_string_new("");
1212      char *buf=NULL;
1213      int sym;
1214
1215      g_scanner_input_text(uzbl.scan, template, strlen(template));
1216      while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1217          token = g_scanner_get_next_token(uzbl.scan);
1218
1219          if(token == G_TOKEN_SYMBOL) {
1220              sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1221              switch(sym) {
1222                  case SYM_URI:
1223                      if(escape_markup) {
1224                          buf = uzbl.state.uri?
1225                              g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
1226                          g_string_append(ret, buf);
1227                          g_free(buf);
1228                      }
1229                      else
1230                          g_string_append(ret, uzbl.state.uri?
1231                                  uzbl.state.uri:g_strdup(""));
1232                      break;
1233                  case SYM_LOADPRGS:
1234                      buf = itos(uzbl.gui.sbar.load_progress);
1235                      g_string_append(ret, buf);
1236                      g_free(buf);
1237                      break;
1238                  case SYM_LOADPRGSBAR:
1239                      buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1240                      g_string_append(ret, buf);
1241                      g_free(buf);
1242                      break;
1243                  case SYM_TITLE:
1244                      if(escape_markup) {
1245                          buf = uzbl.gui.main_title?
1246                              g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
1247                          g_string_append(ret, buf);
1248                          g_free(buf);
1249                      }
1250                      else
1251                          g_string_append(ret, uzbl.gui.main_title?
1252                                  uzbl.gui.main_title:g_strdup(""));
1253                      break;
1254                  case SYM_SELECTED_URI:
1255                      if(escape_markup) {
1256                          buf = uzbl.state.selected_url?
1257                              g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
1258                          g_string_append(ret, buf);
1259                          g_free(buf);
1260                      }
1261                      else
1262                          g_string_append(ret, uzbl.state.selected_url?
1263                                  uzbl.state.selected_url:g_strdup(""));
1264                      break;
1265                  case SYM_NAME:
1266                      buf = itos(uzbl.xwin);
1267                      g_string_append(ret,
1268                              uzbl.state.instance_name?uzbl.state.instance_name:buf);
1269                      g_free(buf);
1270                      break;
1271                  case SYM_KEYCMD:
1272                      if(escape_markup) {
1273                          buf = uzbl.state.keycmd->str?
1274                              g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1275                          g_string_append(ret, buf);
1276                          g_free(buf);
1277                      }
1278                      else
1279                          g_string_append(ret, uzbl.state.keycmd->str?
1280                                  uzbl.state.keycmd->str:g_strdup(""));
1281                      break;
1282                  case SYM_MODE:
1283                      g_string_append(ret,
1284                              uzbl.behave.insert_mode?
1285                              uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1286                      break;
1287                  case SYM_MSG:
1288                      g_string_append(ret,
1289                              uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1290                      break;
1291                      /* useragent syms */
1292                  case SYM_WK_MAJ:
1293                      buf = itos(WEBKIT_MAJOR_VERSION);
1294                      g_string_append(ret, buf);
1295                      g_free(buf);
1296                      break;
1297                  case SYM_WK_MIN:
1298                      buf = itos(WEBKIT_MINOR_VERSION);
1299                      g_string_append(ret, buf);
1300                      g_free(buf);
1301                      break;
1302                  case SYM_WK_MIC:
1303                      buf = itos(WEBKIT_MICRO_VERSION);
1304                      g_string_append(ret, buf);
1305                      g_free(buf);
1306                      break;
1307                  case SYM_SYSNAME:
1308                      g_string_append(ret, uzbl.state.unameinfo.sysname);
1309                      break;
1310                  case SYM_NODENAME:
1311                      g_string_append(ret, uzbl.state.unameinfo.nodename);
1312                      break;
1313                  case SYM_KERNREL:
1314                      g_string_append(ret, uzbl.state.unameinfo.release);
1315                      break;
1316                  case SYM_KERNVER:
1317                      g_string_append(ret, uzbl.state.unameinfo.version);
1318                      break;
1319                  case SYM_ARCHSYS:
1320                      g_string_append(ret, uzbl.state.unameinfo.machine);
1321                      break;
1322                  case SYM_ARCHUZBL:
1323                      g_string_append(ret, ARCH);
1324                      break;
1325 #ifdef _GNU_SOURCE
1326                  case SYM_DOMAINNAME:
1327                      g_string_append(ret, uzbl.state.unameinfo.domainname);
1328                      break;
1329 #endif
1330                  case SYM_COMMIT:
1331                      g_string_append(ret, COMMIT);
1332                      break;
1333                  default:
1334                      break;
1335              }
1336          }
1337          else if(token == G_TOKEN_INT) {
1338              buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1339              g_string_append(ret, buf);
1340              g_free(buf);
1341          }
1342          else if(token == G_TOKEN_IDENTIFIER) {
1343              g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1344          }
1345          else if(token == G_TOKEN_CHAR) {
1346              g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1347          }
1348      }
1349
1350      return g_string_free(ret, FALSE);
1351 }
1352 /* --End Statusbar functions-- */
1353
1354 static void
1355 sharg_append(GArray *a, const gchar *str) {
1356     const gchar *s = (str ? str : "");
1357     g_array_append_val(a, s);
1358 }
1359
1360 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1361 static gboolean
1362 run_command (const gchar *command, const guint npre, const gchar **args,
1363              const gboolean sync, char **output_stdout) {
1364    //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1365     GError *err = NULL;
1366     
1367     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1368     gchar *pid = itos(getpid());
1369     gchar *xwin = itos(uzbl.xwin);
1370     guint i;
1371     sharg_append(a, command);
1372     for (i = 0; i < npre; i++) /* add n args before the default vars */
1373         sharg_append(a, args[i]);
1374     sharg_append(a, uzbl.state.config_file);
1375     sharg_append(a, pid);
1376     sharg_append(a, xwin);
1377     sharg_append(a, uzbl.comm.fifo_path);
1378     sharg_append(a, uzbl.comm.socket_path);
1379     sharg_append(a, uzbl.state.uri);
1380     sharg_append(a, uzbl.gui.main_title);
1381
1382     for (i = npre; i < g_strv_length((gchar**)args); i++)
1383         sharg_append(a, args[i]);
1384     
1385     gboolean result;
1386     if (sync) {
1387         if (*output_stdout) *output_stdout = strfree(*output_stdout);
1388         
1389         result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1390                               NULL, NULL, output_stdout, NULL, NULL, &err);
1391     } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1392                                   NULL, NULL, NULL, &err);
1393
1394     if (uzbl.state.verbose) {
1395         GString *s = g_string_new("spawned:");
1396         for (i = 0; i < (a->len); i++) {
1397             gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1398             g_string_append_printf(s, " %s", qarg);
1399             g_free (qarg);
1400         }
1401         g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1402         printf("%s\n", s->str);
1403         g_string_free(s, TRUE);
1404         if(output_stdout) {
1405             printf("Stdout: %s\n", *output_stdout);
1406         }
1407     }
1408     if (err) {
1409         g_printerr("error on run_command: %s\n", err->message);
1410         g_error_free (err);
1411     }
1412     g_free (pid);
1413     g_free (xwin);
1414     g_array_free (a, TRUE);
1415     return result;
1416 }
1417
1418 static gchar**
1419 split_quoted(const gchar* src, const gboolean unquote) {
1420     /* split on unquoted space, return array of strings;
1421        remove a layer of quotes and backslashes if unquote */
1422     if (!src) return NULL;
1423     
1424     gboolean dq = FALSE;
1425     gboolean sq = FALSE;
1426     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1427     GString *s = g_string_new ("");
1428     const gchar *p;
1429     gchar **ret;
1430     gchar *dup;
1431     for (p = src; *p != '\0'; p++) {
1432         if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1433         else if (*p == '\\') { g_string_append_c(s, *p++);
1434                                g_string_append_c(s, *p); }
1435         else if ((*p == '"') && unquote && !sq) dq = !dq;
1436         else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1437                                      dq = !dq; }
1438         else if ((*p == '\'') && unquote && !dq) sq = !sq;
1439         else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1440                                       sq = ! sq; }
1441         else if ((*p == ' ') && !dq && !sq) {
1442             dup = g_strdup(s->str);
1443             g_array_append_val(a, dup);
1444             g_string_truncate(s, 0);
1445         } else g_string_append_c(s, *p);
1446     }
1447     dup = g_strdup(s->str);
1448     g_array_append_val(a, dup);
1449     ret = (gchar**)a->data;
1450     g_array_free (a, FALSE);
1451     g_string_free (s, TRUE);
1452     return ret;
1453 }
1454
1455 static void
1456 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1457     (void)web_view; (void)result;
1458     //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1459     if (argv_idx(argv, 0))
1460         run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1461 }
1462
1463 static void
1464 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1465     (void)web_view; (void)result;
1466     
1467     if (argv_idx(argv, 0))
1468         run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1469                     TRUE, &uzbl.comm.sync_stdout);
1470 }
1471
1472 static void
1473 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1474     (void)web_view; (void)result;
1475     if (!uzbl.behave.shell_cmd) {
1476         g_printerr ("spawn_sh: shell_cmd is not set!\n");
1477         return;
1478     }
1479     
1480     guint i;
1481     gchar *spacer = g_strdup("");
1482     g_array_insert_val(argv, 1, spacer);
1483     gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1484
1485     for (i = 1; i < g_strv_length(cmd); i++)
1486         g_array_prepend_val(argv, cmd[i]);
1487
1488     if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1489     g_free (spacer);
1490     g_strfreev (cmd);
1491 }
1492
1493 static void
1494 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1495     (void)web_view; (void)result;
1496     if (!uzbl.behave.shell_cmd) {
1497         g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1498         return;
1499     }
1500     
1501     guint i;
1502     gchar *spacer = g_strdup("");
1503     g_array_insert_val(argv, 1, spacer);
1504     gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1505
1506     for (i = 1; i < g_strv_length(cmd); i++)
1507         g_array_prepend_val(argv, cmd[i]);
1508          
1509     if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1510                          TRUE, &uzbl.comm.sync_stdout);
1511     g_free (spacer);
1512     g_strfreev (cmd);
1513 }
1514
1515 static void
1516 parse_command(const char *cmd, const char *param, GString *result) {
1517     CommandInfo *c;
1518
1519     if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1520             guint i;
1521             gchar **par = split_quoted(param, TRUE);
1522             GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1523
1524             if (c->no_split) { /* don't split */
1525                 sharg_append(a, param);
1526             } else if (par) {
1527                 for (i = 0; i < g_strv_length(par); i++)
1528                     sharg_append(a, par[i]);
1529             }
1530
1531             if (result == NULL) {
1532                 GString *result_print = g_string_new("");
1533
1534                 c->function(uzbl.gui.web_view, a, result_print);
1535                 if (result_print->len)
1536                     printf("%*s\n", result_print->len, result_print->str);
1537
1538                 g_string_free(result_print, TRUE);
1539             } else {
1540                 c->function(uzbl.gui.web_view, a, result);
1541             }
1542             g_strfreev (par);
1543             g_array_free (a, TRUE);
1544
1545     } else
1546         g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1547 }
1548
1549 static void
1550 set_proxy_url() {
1551     SoupURI *suri;
1552
1553     if(*uzbl.net.proxy_url == ' '
1554        || uzbl.net.proxy_url == NULL) {
1555         soup_session_remove_feature_by_type(uzbl.net.soup_session,
1556                 (GType) SOUP_SESSION_PROXY_URI);
1557     }
1558     else {
1559         suri = soup_uri_new(uzbl.net.proxy_url);
1560         g_object_set(G_OBJECT(uzbl.net.soup_session),
1561                 SOUP_SESSION_PROXY_URI,
1562                 suri, NULL);
1563         soup_uri_free(suri);
1564     }
1565     return;
1566 }
1567
1568 static void
1569 set_icon() {
1570     if(file_exists(uzbl.gui.icon)) {
1571         if (uzbl.gui.main_window)
1572             gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1573     } else {
1574         g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1575     }
1576     g_free (uzbl.gui.icon);
1577 }
1578
1579 static void
1580 cmd_load_uri() {
1581     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1582     g_array_append_val (a, uzbl.state.uri);
1583     load_uri(uzbl.gui.web_view, a, NULL);
1584     g_array_free (a, TRUE);
1585 }
1586
1587 static void 
1588 cmd_always_insert_mode() {
1589     uzbl.behave.insert_mode =
1590         uzbl.behave.always_insert_mode ?  TRUE : FALSE;
1591     update_title();
1592 }
1593
1594 static void
1595 cmd_max_conns() {
1596     g_object_set(G_OBJECT(uzbl.net.soup_session),
1597             SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1598 }
1599
1600 static void
1601 cmd_max_conns_host() {
1602     g_object_set(G_OBJECT(uzbl.net.soup_session),
1603             SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1604 }
1605
1606 static void
1607 cmd_http_debug() {
1608     soup_session_remove_feature
1609         (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1610     /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1611     /*g_free(uzbl.net.soup_logger);*/
1612
1613     uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1614     soup_session_add_feature(uzbl.net.soup_session,
1615             SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1616 }
1617
1618 static WebKitWebSettings*
1619 view_settings() {
1620     return webkit_web_view_get_settings(uzbl.gui.web_view);
1621 }
1622
1623 static void
1624 cmd_font_size() {
1625     WebKitWebSettings *ws = view_settings();
1626     if (uzbl.behave.font_size > 0) {
1627         g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1628     }
1629     
1630     if (uzbl.behave.monospace_size > 0) {
1631         g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1632                       uzbl.behave.monospace_size, NULL);
1633     } else {
1634         g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1635                       uzbl.behave.font_size, NULL);
1636     }
1637 }
1638
1639 static void
1640 cmd_zoom_level() {
1641     webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1642 }
1643
1644 static void
1645 cmd_disable_plugins() {
1646     g_object_set (G_OBJECT(view_settings()), "enable-plugins", 
1647             !uzbl.behave.disable_plugins, NULL);
1648 }
1649
1650 static void
1651 cmd_disable_scripts() {
1652     g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1653             !uzbl.behave.disable_scripts, NULL);
1654 }
1655
1656 static void
1657 cmd_minimum_font_size() {
1658     g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1659             uzbl.behave.minimum_font_size, NULL);
1660 }
1661 static void
1662 cmd_autoload_img() {
1663     g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1664             uzbl.behave.autoload_img, NULL);
1665 }
1666
1667
1668 static void
1669 cmd_autoshrink_img() {
1670     g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1671             uzbl.behave.autoshrink_img, NULL);
1672 }
1673
1674
1675 static void
1676 cmd_enable_spellcheck() {
1677     g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1678             uzbl.behave.enable_spellcheck, NULL);
1679 }
1680
1681 static void
1682 cmd_enable_private() {
1683     g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1684             uzbl.behave.enable_private, NULL);
1685 }
1686
1687 static void
1688 cmd_print_bg() {
1689     g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1690             uzbl.behave.print_bg, NULL);
1691 }
1692
1693 static void 
1694 cmd_style_uri() {
1695     g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1696             uzbl.behave.style_uri, NULL);
1697 }
1698
1699 static void 
1700 cmd_resizable_txt() {
1701     g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1702             uzbl.behave.resizable_txt, NULL);
1703 }
1704
1705 static void 
1706 cmd_default_encoding() {
1707     g_object_set (G_OBJECT(view_settings()), "default-encoding",
1708             uzbl.behave.default_encoding, NULL);
1709 }
1710
1711 static void 
1712 cmd_enforce_96dpi() {
1713     g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1714             uzbl.behave.enforce_96dpi, NULL);
1715 }
1716
1717 static void 
1718 cmd_caret_browsing() {
1719     g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1720             uzbl.behave.caret_browsing, NULL);
1721 }
1722
1723 static void
1724 cmd_cookie_handler() {
1725     gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1726     /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1727     if ((g_strcmp0(split[0], "sh") == 0) ||
1728         (g_strcmp0(split[0], "spawn") == 0)) {
1729         g_free (uzbl.behave.cookie_handler);
1730         uzbl.behave.cookie_handler =
1731             g_strdup_printf("sync_%s %s", split[0], split[1]);
1732     }
1733     g_strfreev (split);
1734 }
1735
1736 static void
1737 cmd_fifo_dir() {
1738     uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1739 }
1740
1741 static void
1742 cmd_socket_dir() {
1743     uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1744 }
1745
1746 static void
1747 cmd_inject_html() {
1748     if(uzbl.behave.inject_html) {
1749         webkit_web_view_load_html_string (uzbl.gui.web_view,
1750                 uzbl.behave.inject_html, NULL);
1751     }
1752 }
1753
1754 static void
1755 cmd_modkey() {
1756     int i;
1757     char *buf;
1758
1759     buf = g_utf8_strup(uzbl.behave.modkey, -1);
1760     uzbl.behave.modmask = 0;
1761
1762     if(uzbl.behave.modkey) 
1763         g_free(uzbl.behave.modkey);
1764     uzbl.behave.modkey = buf;
1765
1766     for (i = 0; modkeys[i].key != NULL; i++) {
1767         if (g_strrstr(buf, modkeys[i].key))
1768             uzbl.behave.modmask |= modkeys[i].mask;
1769     }
1770 }
1771
1772 static void
1773 cmd_useragent() {
1774     if (*uzbl.net.useragent == ' ') {
1775         g_free (uzbl.net.useragent);
1776         uzbl.net.useragent = NULL;
1777     } else {
1778         gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1779         if (ua)
1780             g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1781         g_free(uzbl.net.useragent);
1782         uzbl.net.useragent = ua;
1783     }
1784 }
1785
1786 static void
1787 move_statusbar() {
1788     gtk_widget_ref(uzbl.gui.scrolled_win);
1789     gtk_widget_ref(uzbl.gui.mainbar);
1790     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1791     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1792
1793     if(uzbl.behave.status_top) {
1794         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1795         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1796     }
1797     else {
1798         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1799         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1800     }
1801     gtk_widget_unref(uzbl.gui.scrolled_win);
1802     gtk_widget_unref(uzbl.gui.mainbar);
1803     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1804     return;
1805 }
1806
1807 static gboolean
1808 set_var_value(gchar *name, gchar *val) {
1809     uzbl_cmdprop *c = NULL;
1810     char *endp = NULL;
1811     char *buf = NULL;
1812
1813     if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1814         /* check for the variable type */
1815         if (c->type == TYPE_STR) {
1816             buf = expand(val, 0);
1817             g_free(*c->ptr);
1818             *c->ptr = buf;
1819         } else if(c->type == TYPE_INT) {
1820             int *ip = (int *)c->ptr;
1821             buf = expand(val, 0);
1822             *ip = (int)strtoul(buf, &endp, 10);
1823             g_free(buf);
1824         } else if (c->type == TYPE_FLOAT) {
1825             float *fp = (float *)c->ptr;
1826             buf = expand(val, 0);
1827             *fp = strtod(buf, &endp);
1828             g_free(buf);
1829         }
1830
1831         /* invoke a command specific function */
1832         if(c->func) c->func();
1833     }
1834     return TRUE;
1835 }
1836
1837 static void
1838 render_html() {
1839     Behaviour *b = &uzbl.behave;
1840
1841     if(b->html_buffer->str) {
1842         webkit_web_view_load_html_string (uzbl.gui.web_view,
1843                 b->html_buffer->str, b->base_url);
1844         g_string_free(b->html_buffer, TRUE);
1845         b->html_buffer = g_string_new("");
1846     }
1847 }
1848
1849 enum {M_CMD, M_HTML};
1850 static void
1851 parse_cmd_line(const char *ctl_line, GString *result) {
1852     Behaviour *b = &uzbl.behave;
1853     size_t len=0;
1854
1855     if(b->mode == M_HTML) {
1856         len = strlen(b->html_endmarker);
1857         /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1858         if(len == strlen(ctl_line)-1 &&
1859            !strncmp(b->html_endmarker, ctl_line, len)) {
1860             set_timeout(0);
1861             set_var_value("mode", "0");
1862             render_html();
1863             return;
1864         }
1865         else {
1866             set_timeout(b->html_timeout);
1867             g_string_append(b->html_buffer, ctl_line);
1868         }
1869     }
1870     else if((ctl_line[0] == '#') /* Comments */
1871             || (ctl_line[0] == ' ')
1872             || (ctl_line[0] == '\n'))
1873         ; /* ignore these lines */
1874     else { /* parse a command */
1875         gchar *ctlstrip;
1876         gchar **tokens = NULL;
1877         len = strlen(ctl_line);
1878
1879         if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1880             ctlstrip = g_strndup(ctl_line, len - 1);
1881         else ctlstrip = g_strdup(ctl_line);
1882
1883         tokens = g_strsplit(ctlstrip, " ", 2);
1884         parse_command(tokens[0], tokens[1], result);
1885         g_free(ctlstrip);
1886         g_strfreev(tokens);
1887     }
1888 }
1889
1890 static gchar*
1891 build_stream_name(int type, const gchar* dir) {
1892     char *xwin_str = NULL;
1893     State *s = &uzbl.state;
1894     gchar *str = NULL;
1895
1896     xwin_str = itos((int)uzbl.xwin);
1897     if (type == FIFO) {
1898         str = g_strdup_printf
1899             ("%s/uzbl_fifo_%s", dir,
1900              s->instance_name ? s->instance_name : xwin_str);
1901     } else if (type == SOCKET) {
1902         str = g_strdup_printf
1903             ("%s/uzbl_socket_%s", dir,
1904              s->instance_name ? s->instance_name : xwin_str );
1905     }
1906     g_free(xwin_str);
1907     return str;
1908 }
1909
1910 static gboolean
1911 control_fifo(GIOChannel *gio, GIOCondition condition) {
1912     if (uzbl.state.verbose)
1913         printf("triggered\n");
1914     gchar *ctl_line;
1915     GIOStatus ret;
1916     GError *err = NULL;
1917
1918     if (condition & G_IO_HUP)
1919         g_error ("Fifo: Read end of pipe died!\n");
1920
1921     if(!gio)
1922        g_error ("Fifo: GIOChannel broke\n");
1923
1924     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1925     if (ret == G_IO_STATUS_ERROR) {
1926         g_error ("Fifo: Error reading: %s\n", err->message);
1927         g_error_free (err);
1928     }
1929
1930     parse_cmd_line(ctl_line, NULL);
1931     g_free(ctl_line);
1932
1933     return TRUE;
1934 }
1935
1936 static gchar*
1937 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1938     if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1939         if (unlink(uzbl.comm.fifo_path) == -1)
1940             g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1941         g_free(uzbl.comm.fifo_path);
1942         uzbl.comm.fifo_path = NULL;
1943     }
1944
1945     if (*dir == ' ') { /* space unsets the variable */
1946         g_free (dir);
1947         return NULL;
1948     }
1949
1950     GIOChannel *chan = NULL;
1951     GError *error = NULL;
1952     gchar *path = build_stream_name(FIFO, dir);
1953
1954     if (!file_exists(path)) {
1955         if (mkfifo (path, 0666) == 0) {
1956             // 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.
1957             chan = g_io_channel_new_file(path, "r+", &error);
1958             if (chan) {
1959                 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1960                     if (uzbl.state.verbose)
1961                         printf ("init_fifo: created successfully as %s\n", path);
1962                     uzbl.comm.fifo_path = path;
1963                     return dir;
1964                 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1965             } else g_warning ("init_fifo: can't open: %s\n", error->message);
1966         } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1967     } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1968
1969     /* if we got this far, there was an error; cleanup */
1970     if (error) g_error_free (error);
1971     g_free(dir);
1972     g_free(path);
1973     return NULL;
1974 }
1975
1976 static gboolean
1977 control_stdin(GIOChannel *gio, GIOCondition condition) {
1978     (void) condition;
1979     gchar *ctl_line = NULL;
1980     GIOStatus ret;
1981
1982     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1983     if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1984         return FALSE;
1985
1986     parse_cmd_line(ctl_line, NULL);
1987     g_free(ctl_line);
1988
1989     return TRUE;
1990 }
1991
1992 static void
1993 create_stdin () {
1994     GIOChannel *chan = NULL;
1995     GError *error = NULL;
1996
1997     chan = g_io_channel_unix_new(fileno(stdin));
1998     if (chan) {
1999         if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
2000             g_error ("Stdin: could not add watch\n");
2001         } else {
2002             if (uzbl.state.verbose)
2003                 printf ("Stdin: watch added successfully\n");
2004         }
2005     } else {
2006         g_error ("Stdin: Error while opening: %s\n", error->message);
2007     }
2008     if (error) g_error_free (error);
2009 }
2010
2011 static gboolean
2012 control_socket(GIOChannel *chan) {
2013     struct sockaddr_un remote;
2014     unsigned int t = sizeof(remote);
2015     int clientsock;
2016     GIOChannel *clientchan;
2017
2018     clientsock = accept (g_io_channel_unix_get_fd(chan),
2019                          (struct sockaddr *) &remote, &t);
2020     
2021     if ((clientchan = g_io_channel_unix_new(clientsock))) {
2022         g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2023                        (GIOFunc) control_client_socket, clientchan);
2024     }
2025
2026     return TRUE;
2027 }
2028
2029 static gboolean
2030 control_client_socket(GIOChannel *clientchan) {
2031     char *ctl_line;
2032     GString *result = g_string_new("");
2033     GError *error = NULL;
2034     GIOStatus ret;
2035     gsize len;
2036
2037     ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2038     if (ret == G_IO_STATUS_ERROR) {
2039         g_warning ("Error reading: %s\n", error->message);
2040         g_io_channel_shutdown(clientchan, TRUE, &error);
2041         return FALSE;
2042     } else if (ret == G_IO_STATUS_EOF) {
2043         /* shutdown and remove channel watch from main loop */
2044         g_io_channel_shutdown(clientchan, TRUE, &error);
2045         return FALSE;
2046     }
2047
2048     if (ctl_line) {
2049         parse_cmd_line (ctl_line, result);
2050         g_string_append_c(result, '\n');
2051         ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2052                                         &len, &error);
2053         if (ret == G_IO_STATUS_ERROR) {
2054             g_warning ("Error writing: %s", error->message);
2055         }
2056         g_io_channel_flush(clientchan, &error);
2057     }
2058
2059     if (error) g_error_free (error);
2060     g_string_free(result, TRUE);
2061     g_free(ctl_line);
2062     return TRUE;
2063 }
2064
2065 static gchar*
2066 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2067     if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2068         if (unlink(uzbl.comm.socket_path) == -1)
2069             g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2070         g_free(uzbl.comm.socket_path);
2071         uzbl.comm.socket_path = NULL;
2072     }
2073
2074     if (*dir == ' ') {
2075         g_free(dir);
2076         return NULL;
2077     }
2078
2079     GIOChannel *chan = NULL;
2080     int sock, len;
2081     struct sockaddr_un local;
2082     gchar *path = build_stream_name(SOCKET, dir);
2083
2084     sock = socket (AF_UNIX, SOCK_STREAM, 0);
2085
2086     local.sun_family = AF_UNIX;
2087     strcpy (local.sun_path, path);
2088     unlink (local.sun_path);
2089
2090     len = strlen (local.sun_path) + sizeof (local.sun_family);
2091     if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2092         if (uzbl.state.verbose)
2093             printf ("init_socket: opened in %s\n", path);
2094         listen (sock, 5);
2095
2096         if( (chan = g_io_channel_unix_new(sock)) ) {
2097             g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2098             uzbl.comm.socket_path = path;
2099             return dir;
2100         }
2101     } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2102
2103     /* if we got this far, there was an error; cleanup */
2104     g_free(path);
2105     g_free(dir);
2106     return NULL;
2107 }
2108
2109 /*
2110  NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2111  it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2112 */
2113 // this function may be called very early when the templates are not set (yet), hence the checks
2114 static void
2115 update_title (void) {
2116     Behaviour *b = &uzbl.behave;
2117     gchar *parsed;
2118
2119     if (b->show_status) {
2120         if (b->title_format_short) {
2121             parsed = expand_template(b->title_format_short, FALSE);
2122             if (uzbl.gui.main_window)
2123                 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2124             g_free(parsed);
2125         }
2126         if (b->status_format) {
2127             parsed = expand_template(b->status_format, TRUE);
2128             gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2129             g_free(parsed);
2130         }
2131         if (b->status_background) {
2132             GdkColor color;
2133             gdk_color_parse (b->status_background, &color);
2134             //labels and hboxes do not draw their own background. applying this on the window/vbox is ok as the statusbar is the only affected widget. (if not, we could also use GtkEventBox)
2135             if (uzbl.gui.main_window)
2136                     gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2137             else if (uzbl.gui.plug)
2138                     gtk_widget_modify_bg ((GtkWidget * ) uzbl.gui.plug, GTK_STATE_NORMAL, &color);
2139         }
2140     } else {
2141         if (b->title_format_long) {
2142             parsed = expand_template(b->title_format_long, FALSE);
2143             if (uzbl.gui.main_window)
2144                 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2145             g_free(parsed);
2146         }
2147     }
2148 }
2149
2150 static gboolean
2151 configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
2152     (void) window;
2153     (void) event;
2154
2155     retreive_geometry();
2156     return FALSE;
2157 }
2158
2159 static 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 static 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 static 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 static 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 static 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 static
2318 GtkWidget* 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     g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2325
2326     return window;
2327 }
2328
2329 static
2330 GtkPlug* create_plug () {
2331     GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2332     g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2333     g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2334
2335     return plug;
2336 }
2337
2338
2339 static gchar**
2340 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2341     /*
2342       If actname is one that calls an external command, this function will inject
2343       newargs in front of the user-provided args in that command line.  They will
2344       come become after the body of the script (in sh) or after the name of
2345       the command to execute (in spawn).
2346       i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2347       span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2348
2349       The return value consist of two strings: the action (sh, ...) and its args.
2350
2351       If act is not one that calls an external command, then the given action merely
2352       gets duplicated.
2353     */
2354     GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
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 static 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 static 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 static 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 static 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 static 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 static 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'", 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 static 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'", 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 static void
2590 hide_window_cb(GtkWidget *widget, gpointer data) {
2591     (void) data;
2592
2593     gtk_widget_hide(widget);
2594 }
2595
2596 static 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 static 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 static gboolean
2634 inspector_close_window_cb (WebKitWebInspector* inspector){
2635     (void) inspector;
2636     return TRUE;
2637 }
2638
2639 static gboolean
2640 inspector_attach_window_cb (WebKitWebInspector* inspector){
2641     (void) inspector;
2642     return FALSE;
2643 }
2644
2645 static gboolean
2646 inspector_detach_window_cb (WebKitWebInspector* inspector){
2647     (void) inspector;
2648     return FALSE;
2649 }
2650
2651 static gboolean
2652 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2653     (void) inspector;
2654     return FALSE;
2655 }
2656
2657 static gboolean
2658 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2659     (void) inspector;
2660     return FALSE;
2661 }
2662
2663 static 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 static 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 }
2693
2694 static void
2695 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2696     (void) ud;
2697     Action *a = v;
2698
2699     printf("bind %s = %s %s\n", (char *)k ,
2700             (char *)a->name, a->param?(char *)a->param:"");
2701 }
2702
2703 static void
2704 dump_config() {
2705     g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2706     g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2707 }
2708
2709 static void
2710 retreive_geometry() {
2711     int w, h, x, y;
2712     GString *buf = g_string_new("");
2713
2714     gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2715     gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2716
2717     g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2718     uzbl.gui.geometry = g_string_free(buf, FALSE);
2719 }
2720
2721 /** -- MAIN -- **/
2722 int
2723 main (int argc, char* argv[]) {
2724     gtk_init (&argc, &argv);
2725     if (!g_thread_supported ())
2726         g_thread_init (NULL);
2727     uzbl.state.executable_path = g_strdup(argv[0]);
2728     uzbl.state.selected_url = NULL;
2729     uzbl.state.searchtx = NULL;
2730
2731     GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2732     g_option_context_add_main_entries (context, entries, NULL);
2733     g_option_context_add_group (context, gtk_get_option_group (TRUE));
2734     g_option_context_parse (context, &argc, &argv, NULL);
2735     g_option_context_free(context);
2736     
2737     gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2738     gboolean verbose_override = uzbl.state.verbose;
2739
2740     /* initialize hash table */
2741     uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2742
2743     uzbl.net.soup_session = webkit_get_default_session();
2744     uzbl.state.keycmd = g_string_new("");
2745
2746     if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2747         fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2748     if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2749         fprintf(stderr, "uzbl: error hooking SIGINT\n");
2750     if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2751         fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2752
2753
2754     if(uname(&uzbl.state.unameinfo) == -1)
2755         g_printerr("Can't retrieve unameinfo.  Your useragent might appear wrong.\n");
2756
2757     uzbl.gui.sbar.progress_s = g_strdup("=");
2758     uzbl.gui.sbar.progress_u = g_strdup("·");
2759     uzbl.gui.sbar.progress_w = 10;
2760
2761     /* HTML mode defaults*/
2762     uzbl.behave.html_buffer = g_string_new("");
2763     uzbl.behave.html_endmarker = g_strdup(".");
2764     uzbl.behave.html_timeout = 60;
2765     uzbl.behave.base_url = g_strdup("http://invalid");
2766
2767     /* default mode indicators */
2768     uzbl.behave.insert_indicator = g_strdup("I");
2769     uzbl.behave.cmd_indicator    = g_strdup("C");
2770
2771     setup_scanner();
2772     commands_hash ();
2773     make_var_to_name_hash();
2774
2775     uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2776
2777     uzbl.gui.scrolled_win = create_browser();
2778     create_mainbar();
2779
2780     /* initial packing */
2781     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2782     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2783
2784     if (uzbl.state.socket_id) {
2785         uzbl.gui.plug = create_plug ();
2786         gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2787         gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2788     } else {
2789         uzbl.gui.main_window = create_window ();
2790         gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2791         gtk_widget_show_all (uzbl.gui.main_window);
2792         uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2793     }
2794
2795     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2796
2797     if (uzbl.state.verbose) {
2798         printf("Uzbl start location: %s\n", argv[0]);
2799         if (uzbl.state.socket_id)
2800             printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2801         else
2802             printf("window_id %i\n",(int) uzbl.xwin);
2803         printf("pid %i\n", getpid ());
2804         printf("name: %s\n", uzbl.state.instance_name);
2805     }
2806
2807     uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2808     uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2809     uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2810     uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2811     gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2812
2813     if(uzbl.gui.geometry)
2814         cmd_set_geometry();
2815     else
2816         retreive_geometry();
2817
2818     settings_init ();
2819
2820     if (!uzbl.behave.show_status)
2821         gtk_widget_hide(uzbl.gui.mainbar);
2822     else
2823         update_title();
2824
2825     /* WebInspector */
2826     set_up_inspector();
2827
2828     if (verbose_override > uzbl.state.verbose)
2829         uzbl.state.verbose = verbose_override;
2830     
2831     if (uri_override) {
2832         set_var_value("uri", uri_override);
2833         g_free(uri_override);
2834     } else if (uzbl.state.uri)
2835         cmd_load_uri(uzbl.gui.web_view, NULL);
2836
2837     gtk_main ();
2838     clean_up();
2839
2840     return EXIT_SUCCESS;
2841 }
2842
2843 /* vi: set et ts=4: */