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