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