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