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