use expand instead of expand_template for useragent.
[uzbl-mobile] / uzbl.c
1 /* -*- c-basic-offset: 4; -*- */
2 // Original code taken from the example webkit-gtk+ application. see notice below.
3 // Modified code is licensed under the GPL 3.  See LICENSE file.
4
5
6 /*
7  * Copyright (C) 2006, 2007 Apple Inc.
8  * Copyright (C) 2007 Alp Toker <alp@atoker.com>
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
20  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32
33 #define LENGTH(x) (sizeof x / sizeof x[0])
34 #define MAX_BINDINGS 256
35 #define _POSIX_SOURCE
36
37 #include <gtk/gtk.h>
38 #include <gdk/gdkx.h>
39 #include <gdk/gdkkeysyms.h>
40 #include <sys/socket.h>
41 #include <sys/stat.h>
42 #include <sys/types.h>
43 #include <sys/un.h>
44 #include <sys/utsname.h>
45 #include <sys/time.h>
46 #include <webkit/webkit.h>
47 #include <libsoup/soup.h>
48 #include <JavaScriptCore/JavaScript.h>
49
50 #include <stdio.h>
51 #include <string.h>
52 #include <unistd.h>
53 #include <stdlib.h>
54 #include <errno.h>
55 #include <fcntl.h>
56 #include <signal.h>
57 #include "uzbl.h"
58 #include "config.h"
59
60 Uzbl uzbl;
61
62
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 *name;
161     struct {
162       void *ptr;
163       int type;
164     } cp;
165 } const_name_to_ptr[] = {
166     { "WEBKIT_MAJOR",  {(void*)WEBKIT_MAJOR_VERSION,        TYPE_INT}},
167     { "WEBKIT_MINOR",  {(void*)WEBKIT_MINOR_VERSION,        TYPE_INT}},
168     { "WEBKIT_MICRO",  {(void*)WEBKIT_MICRO_VERSION,        TYPE_INT}},
169     { "SYSNAME",       {&(uzbl.state.unameinfo.sysname),    TYPE_STR}},
170     { "NODENAME",      {&(uzbl.state.unameinfo.nodename),   TYPE_STR}},
171     { "KERNREL",       {&(uzbl.state.unameinfo.release),    TYPE_STR}},
172     { "KERNVER",       {&(uzbl.state.unameinfo.version),    TYPE_STR}},
173     { "ARCH_SYSTEM",   {&(uzbl.state.unameinfo.machine),    TYPE_STR}},
174     { "ARCH_UZBL",     {&(ARCH),                            TYPE_STR}},
175 #ifdef _GNU_SOURCE
176     { "DOMAINNAME",    {&(uzbl.state.unameinfo.domainname), TYPE_STR}},
177 #endif
178     { "COMMIT",        {&(COMMIT),                          TYPE_STR}},
179
180     { NULL,            {NULL,                               TYPE_INT}}
181 }, *n2c_p = const_name_to_ptr;
182
183 const struct {
184     char *key;
185     guint mask;
186 } modkeys[] = {
187     { "SHIFT",   GDK_SHIFT_MASK   }, // shift
188     { "LOCK",    GDK_LOCK_MASK    }, // capslock or shiftlock, depending on xserver's modmappings
189     { "CONTROL", GDK_CONTROL_MASK }, // control
190     { "MOD1",    GDK_MOD1_MASK    }, // 4th mod - normally alt but depends on modmappings
191     { "MOD2",    GDK_MOD2_MASK    }, // 5th mod
192     { "MOD3",    GDK_MOD3_MASK    }, // 6th mod
193     { "MOD4",    GDK_MOD4_MASK    }, // 7th mod
194     { "MOD5",    GDK_MOD5_MASK    }, // 8th mod
195     { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
196     { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
197     { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
198     { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
199     { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
200     { "SUPER",   GDK_SUPER_MASK   }, // super (since 2.10)
201     { "HYPER",   GDK_HYPER_MASK   }, // hyper (since 2.10)
202     { "META",    GDK_META_MASK    }, // meta (since 2.10)
203     { NULL,      0                }
204 };
205
206
207 /* construct a hash from the var_name_to_ptr and the const_name_to_ptr array
208  * for quick access */
209 void
210 make_var_to_name_hash() {
211     uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
212     while(n2v_p->name) {
213         g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
214         n2v_p++;
215     }
216
217     uzbl.comm.proto_const = g_hash_table_new(g_str_hash, g_str_equal);
218     while(n2c_p->name) {
219         g_hash_table_insert(uzbl.comm.proto_const, n2c_p->name, (gpointer) &n2c_p->cp);
220         n2c_p++;
221     }
222 }
223
224 /* --- UTILITY FUNCTIONS --- */
225
226 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS};
227 static guint
228 get_exp_type(gchar *s) {
229     /* variables */
230     if(*(s+1) == '(')
231         return EXP_EXPR;
232     else if(*(s+1) == '{')
233         return EXP_BRACED_VAR;
234     else if(*(s+1) == '<')
235         return EXP_JS;
236     else
237         return EXP_SIMPLE_VAR;
238
239 return EXP_ERR;
240 }
241
242 /* 
243  * recurse == 1: don't expand '@(command)@'
244  * recurse == 2: don't expand '@<java script>@'
245  */
246 gchar *
247 expand(char *s, guint recurse) {
248     uzbl_cmdprop *c;
249     guint etype;
250     char upto = ' ';
251     char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
252     char str_end[2];
253     char ret[4096];
254     char *vend;
255     GError *err = NULL;
256     gchar *cmd_stdout = NULL;
257     gchar *mycmd = NULL;
258     GString *buf = g_string_new("");
259     GString *js_ret = g_string_new("");
260
261     while(*s) {
262         switch(*s) {
263             case '\\':
264                 g_string_append_c(buf, *++s);
265                 s++;
266                 break;
267
268             case '@':
269                 etype = get_exp_type(s);
270                 s++;
271
272                 switch(etype) {
273                     case EXP_SIMPLE_VAR:
274                         if( (vend = strpbrk(s, end_simple_var)) ||
275                             (vend = strchr(s, '\0')) ) {
276                             strncpy(ret, s, vend-s);
277                             ret[vend-s] = '\0';
278                         }
279                         break;
280                     case EXP_BRACED_VAR:
281                         s++; upto = '}';
282                         if( (vend = strchr(s, upto)) ||
283                             (vend = strchr(s, '\0')) ) {
284                             strncpy(ret, s, vend-s);
285                             ret[vend-s] = '\0';
286                         }
287                         break;
288                     case EXP_EXPR:
289                         s++;
290                         strcpy(str_end, ")@");
291                         str_end[2] = '\0';
292                         if( (vend = strstr(s, str_end)) ||
293                             (vend = strchr(s, '\0')) ) {
294                             strncpy(ret, s, vend-s);
295                             ret[vend-s] = '\0';
296                         }
297                         break;
298                     case EXP_JS: 
299                         s++;
300                         strcpy(str_end, ">@");
301                         str_end[2] = '\0';
302                         if( (vend = strstr(s, str_end)) ||
303                             (vend = strchr(s, '\0')) ) {
304                             strncpy(ret, s, vend-s);
305                             ret[vend-s] = '\0';
306                         }
307                         break;
308                 }
309
310                 if(etype == EXP_SIMPLE_VAR ||
311                    etype == EXP_BRACED_VAR) {
312                     void *ptr;
313
314                     if('A' <= ret[0] && 'Z' >= ret[0] &&
315                        (c = g_hash_table_lookup(uzbl.comm.proto_const, ret))) {
316                         ptr = c->ptr;
317                     } else if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
318                         ptr = *c->ptr;
319                     }
320
321                     if(c && c->type == TYPE_STR) {
322                         g_string_append(buf, (gchar *)ptr);
323                     } else if(c && c->type == TYPE_INT) {
324                         char *b = itos((uintptr_t)ptr);
325                         g_string_append(buf, b);
326                         g_free(b);
327                     }
328
329                     if(etype == EXP_SIMPLE_VAR)
330                         s = vend;
331                     else
332                         s = vend+1;
333                 }
334                 else if(recurse != 1 && 
335                         etype == EXP_EXPR) {
336                     mycmd = expand(ret, 1);
337                     g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
338                     g_free(mycmd);
339
340                     if (err) {
341                         g_printerr("error on running command: %s\n", err->message);
342                         g_error_free (err);
343                     }
344                     else if (*cmd_stdout) {
345                         g_string_append(buf, cmd_stdout);
346                         g_free(cmd_stdout);
347                     }
348                     s = vend+2;
349                 }
350                 else if(recurse != 2 && 
351                         etype == EXP_JS) {
352                     mycmd = expand(ret, 2);
353                     eval_js(uzbl.gui.web_view, mycmd, js_ret);
354                     g_free(mycmd);
355
356                     if(js_ret->str) {
357                         g_string_append(buf, js_ret->str);
358                         g_string_free(js_ret, TRUE);
359                         js_ret = g_string_new("");
360                     }
361                     s = vend+2;
362                 }
363                 break;
364
365             default:
366                 g_string_append_c(buf, *s);
367                 s++;
368                 break;
369         }
370     }
371     g_string_free(js_ret, TRUE);
372     return g_string_free(buf, FALSE);
373 }
374
375 char *
376 itos(int val) {
377     char tmp[20];
378
379     snprintf(tmp, sizeof(tmp), "%i", val);
380     return g_strdup(tmp);
381 }
382
383 static gchar*
384 strfree(gchar *str) { g_free(str); return NULL; }  // for freeing & setting to null in one go
385
386 static gchar*
387 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
388
389 static char *
390 str_replace (const char* search, const char* replace, const char* string) {
391     gchar **buf;
392     char *ret;
393
394     buf = g_strsplit (string, search, -1);
395     ret = g_strjoinv (replace, buf);
396     g_strfreev(buf); // somebody said this segfaults
397
398     return ret;
399 }
400
401 static GArray*
402 read_file_by_line (gchar *path) {
403     GIOChannel *chan = NULL;
404     gchar *readbuf = NULL;
405     gsize len;
406     GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
407     int i = 0;
408     
409     chan = g_io_channel_new_file(path, "r", NULL);
410     
411     if (chan) {
412         while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
413             const gchar* val = g_strdup (readbuf);
414             g_array_append_val (lines, val);
415             g_free (readbuf);
416             i ++;
417         }
418         
419         g_io_channel_unref (chan);
420     } else {
421         fprintf(stderr, "File '%s' not be read.\n", path);
422     }
423     
424     return lines;
425 }
426
427 static
428 gchar* parseenv (char* string) {
429     extern char** environ;
430     gchar* tmpstr = NULL;
431     int i = 0;
432     
433
434     while (environ[i] != NULL) {
435         gchar** env = g_strsplit (environ[i], "=", 2);
436         gchar* envname = g_strconcat ("$", env[0], NULL);
437
438         if (g_strrstr (string, envname) != NULL) {
439             tmpstr = g_strdup(string);
440             g_free (string);
441             string = str_replace(envname, env[1], tmpstr);
442             g_free (tmpstr);
443         }
444
445         g_free (envname);
446         g_strfreev (env); // somebody said this breaks uzbl
447         i++;
448     }
449
450     return string;
451 }
452
453 static sigfunc*
454 setup_signal(int signr, sigfunc *shandler) {
455     struct sigaction nh, oh;
456
457     nh.sa_handler = shandler;
458     sigemptyset(&nh.sa_mask);
459     nh.sa_flags = 0;
460
461     if(sigaction(signr, &nh, &oh) < 0)
462         return SIG_ERR;
463
464     return NULL;
465 }
466
467 static void
468 clean_up(void) {
469     if (uzbl.behave.fifo_dir)
470         unlink (uzbl.comm.fifo_path);
471     if (uzbl.behave.socket_dir)
472         unlink (uzbl.comm.socket_path);
473
474     g_free(uzbl.state.executable_path);
475     g_string_free(uzbl.state.keycmd, TRUE);
476     g_hash_table_destroy(uzbl.bindings);
477     g_hash_table_destroy(uzbl.behave.commands);
478 }
479
480 /* used for html_mode_timeout 
481  * be sure to extend this function to use
482  * more timers if needed in other places
483 */
484 static void
485 set_timeout(int seconds) {
486     struct itimerval t;
487     memset(&t, 0, sizeof t);
488
489     t.it_value.tv_sec =  seconds;
490     t.it_value.tv_usec = 0;
491     setitimer(ITIMER_REAL, &t, NULL);
492 }
493
494 /* --- SIGNAL HANDLER --- */
495
496 static void
497 catch_sigterm(int s) {
498     (void) s;
499     clean_up();
500 }
501
502 static void
503 catch_sigint(int s) {
504     (void) s;
505     clean_up();
506     exit(EXIT_SUCCESS);
507 }
508
509 static void
510 catch_alrm(int s) {
511     (void) s;
512
513     set_var_value("mode", "0");
514     render_html();
515 }
516
517
518 /* --- CALLBACKS --- */
519
520 static gboolean
521 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
522     (void) web_view;
523     (void) frame;
524     (void) navigation_action;
525     (void) policy_decision;
526     (void) user_data;
527     const gchar* uri = webkit_network_request_get_uri (request);
528     if (uzbl.state.verbose)
529         printf("New window requested -> %s \n", uri);
530     new_window_load_uri(uri);
531     return (FALSE);
532 }
533
534 static gboolean
535 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type,  WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
536     (void) frame;
537     (void) request;
538     (void) user_data;
539
540     /* If we can display it, let's display it... */
541     if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
542         webkit_web_policy_decision_use (policy_decision);
543         return TRUE;
544     }
545
546     /* ...everything we can't displayed is downloaded */
547     webkit_web_policy_decision_download (policy_decision);
548     return TRUE;
549 }
550
551 WebKitWebView*
552 create_web_view_cb (WebKitWebView  *web_view, WebKitWebFrame *frame, gpointer user_data) {
553     (void) web_view;
554     (void) frame;
555     (void) user_data;
556     if (uzbl.state.selected_url != NULL) {
557         if (uzbl.state.verbose)
558             printf("\nNew web view -> %s\n",uzbl.state.selected_url);
559         new_window_load_uri(uzbl.state.selected_url);
560     } else {
561         if (uzbl.state.verbose)
562             printf("New web view -> %s\n","Nothing to open, exiting");
563     }
564     return (NULL);
565 }
566
567 static gboolean
568 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
569     (void) web_view;
570     (void) user_data;
571     if (uzbl.behave.download_handler) {
572         const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
573         if (uzbl.state.verbose)
574             printf("Download -> %s\n",uri);
575         /* if urls not escaped, we may have to escape and quote uri before this call */
576         run_handler(uzbl.behave.download_handler, uri);
577     }
578     return (FALSE);
579 }
580
581 /* scroll a bar in a given direction */
582 static void
583 scroll (GtkAdjustment* bar, GArray *argv) {
584     gchar *end;
585     gdouble max_value;
586
587     gdouble page_size = gtk_adjustment_get_page_size(bar);
588     gdouble value = gtk_adjustment_get_value(bar);
589     gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
590
591     if (*end == '%')
592         value += page_size * amount * 0.01;
593     else
594         value += amount;
595
596     max_value = gtk_adjustment_get_upper(bar) - page_size;
597
598     if (value > max_value)
599         value = max_value; /* don't scroll past the end of the page */
600
601     gtk_adjustment_set_value (bar, value);
602 }
603
604 static void
605 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
606     (void) page; (void) argv; (void) result;
607     gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
608 }
609
610 static void
611 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
612     (void) page; (void) argv; (void) result;
613     gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
614                               gtk_adjustment_get_page_size(uzbl.gui.bar_v));
615 }
616
617 static void
618 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
619     (void) page; (void) result;
620     scroll(uzbl.gui.bar_v, argv);
621 }
622
623 static void
624 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
625     (void) page; (void) result;
626     scroll(uzbl.gui.bar_h, argv);
627 }
628
629 static void
630 cmd_set_status() {
631     if (!uzbl.behave.show_status) {
632         gtk_widget_hide(uzbl.gui.mainbar);
633     } else {
634         gtk_widget_show(uzbl.gui.mainbar);
635     }
636     update_title();
637 }
638
639 static void
640 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
641     (void)page;
642     (void)argv;
643     (void)result;
644
645     webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
646 }
647
648 static void
649 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
650     (void)page;
651     (void)argv;
652     (void)result;
653
654     if (uzbl.behave.show_status) {
655         gtk_widget_hide(uzbl.gui.mainbar);
656     } else {
657         gtk_widget_show(uzbl.gui.mainbar);
658     }
659     uzbl.behave.show_status = !uzbl.behave.show_status;
660     update_title();
661 }
662
663 static void
664 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
665     (void) page;
666     (void) title;
667     (void) data;
668     //Set selected_url state variable
669     g_free(uzbl.state.selected_url);
670     uzbl.state.selected_url = NULL;
671     if (link) {
672         uzbl.state.selected_url = g_strdup(link);
673     }
674     update_title();
675 }
676
677 static void
678 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
679     (void) web_view;
680     (void) param_spec;
681     const gchar *title = webkit_web_view_get_title(web_view);
682     if (uzbl.gui.main_title)
683         g_free (uzbl.gui.main_title);
684     uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
685     update_title();
686 }
687
688 static void
689 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
690     (void) page;
691     (void) data;
692     uzbl.gui.sbar.load_progress = progress;
693     update_title();
694 }
695
696 static void
697 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
698     (void) page;
699     (void) frame;
700     (void) data;
701     if (uzbl.behave.load_finish_handler)
702         run_handler(uzbl.behave.load_finish_handler, "");
703 }
704
705 static void
706 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
707     (void) page;
708     (void) frame;
709     (void) data;
710     uzbl.gui.sbar.load_progress = 0;
711     g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
712     if (uzbl.behave.load_start_handler)
713         run_handler(uzbl.behave.load_start_handler, "");
714 }
715
716 static void
717 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
718     (void) page;
719     (void) data;
720     g_free (uzbl.state.uri);
721     GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
722     uzbl.state.uri = g_string_free (newuri, FALSE);
723     if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
724         uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
725         update_title();
726     }
727     if (uzbl.behave.load_commit_handler)
728         run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
729 }
730
731 static void
732 destroy_cb (GtkWidget* widget, gpointer data) {
733     (void) widget;
734     (void) data;
735     gtk_main_quit ();
736 }
737
738 static void
739 log_history_cb () {
740    if (uzbl.behave.history_handler) {
741        time_t rawtime;
742        struct tm * timeinfo;
743        char date [80];
744        time ( &rawtime );
745        timeinfo = localtime ( &rawtime );
746        strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
747        run_handler(uzbl.behave.history_handler, date);
748    }
749 }
750
751
752 /* VIEW funcs (little webkit wrappers) */
753 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
754 VIEWFUNC(reload)
755 VIEWFUNC(reload_bypass_cache)
756 VIEWFUNC(stop_loading)
757 VIEWFUNC(zoom_in)
758 VIEWFUNC(zoom_out)
759 VIEWFUNC(go_back)
760 VIEWFUNC(go_forward)
761 #undef VIEWFUNC
762
763 /* -- command to callback/function map for things we cannot attach to any signals */
764 static struct {char *key; CommandInfo value;} cmdlist[] =
765 {   /* key                   function      no_split      */
766     { "back",               {view_go_back, 0}              },
767     { "forward",            {view_go_forward, 0}           },
768     { "scroll_vert",        {scroll_vert, 0}               },
769     { "scroll_horz",        {scroll_horz, 0}               },
770     { "scroll_begin",       {scroll_begin, 0}              },
771     { "scroll_end",         {scroll_end, 0}                },
772     { "reload",             {view_reload, 0},              },
773     { "reload_ign_cache",   {view_reload_bypass_cache, 0}  },
774     { "stop",               {view_stop_loading, 0},        },
775     { "zoom_in",            {view_zoom_in, 0},             }, //Can crash (when max zoom reached?).
776     { "zoom_out",           {view_zoom_out, 0},            },
777     { "toggle_zoom_type",   {toggle_zoom_type, 0},         },
778     { "uri",                {load_uri, TRUE}               },
779     { "js",                 {run_js, TRUE}                 },
780     { "script",             {run_external_js, 0}           },
781     { "toggle_status",      {toggle_status_cb, 0}          },
782     { "spawn",              {spawn, 0}                     },
783     { "sync_spawn",         {spawn_sync, 0}                }, // needed for cookie handler
784     { "sh",                 {spawn_sh, 0}                  },
785     { "sync_sh",            {spawn_sh_sync, 0}             }, // needed for cookie handler
786     { "exit",               {close_uzbl, 0}                },
787     { "search",             {search_forward_text, TRUE}    },
788     { "search_reverse",     {search_reverse_text, TRUE}    },
789     { "dehilight",          {dehilight, 0}                 },
790     { "toggle_insert_mode", {toggle_insert_mode, 0}        },
791     { "set",                {set_var, TRUE}                },
792   //{ "get",                {get_var, TRUE}                },
793     { "bind",               {act_bind, TRUE}               },
794     { "dump_config",        {act_dump_config, 0}           },
795     { "keycmd",             {keycmd, TRUE}                 },
796     { "keycmd_nl",          {keycmd_nl, TRUE}              },
797     { "keycmd_bs",          {keycmd_bs, 0}                 },
798     { "chain",              {chain, 0}                     },
799     { "print",              {print, TRUE}                  }
800 };
801
802 static void
803 commands_hash(void)
804 {
805     unsigned int i;
806     uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
807
808     for (i = 0; i < LENGTH(cmdlist); i++)
809         g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
810 }
811
812 /* -- CORE FUNCTIONS -- */
813
814 void
815 free_action(gpointer act) {
816     Action *action = (Action*)act;
817     g_free(action->name);
818     if (action->param)
819         g_free(action->param);
820     g_free(action);
821 }
822
823 Action*
824 new_action(const gchar *name, const gchar *param) {
825     Action *action = g_new(Action, 1);
826
827     action->name = g_strdup(name);
828     if (param)
829         action->param = g_strdup(param);
830     else
831         action->param = NULL;
832
833     return action;
834 }
835
836 static bool
837 file_exists (const char * filename) {
838     return (access(filename, F_OK) == 0);
839 }
840
841 static void
842 set_var(WebKitWebView *page, GArray *argv, GString *result) {
843     (void) page; (void) result;
844     gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
845     if (split[0] != NULL) {
846         gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
847         set_var_value(g_strstrip(split[0]), value);
848         g_free(value);
849     }
850     g_strfreev(split);
851 }
852
853 static void
854 print(WebKitWebView *page, GArray *argv, GString *result) {
855     (void) page; (void) result;
856     gchar* buf;
857
858     buf = expand(argv_idx(argv, 0), 0);
859     g_string_assign(result, buf);
860     g_free(buf);
861 }
862
863 static void
864 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
865     (void) page; (void) result;
866     gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
867     gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
868     add_binding(g_strstrip(split[0]), value);
869     g_free(value);
870     g_strfreev(split);
871 }
872
873
874 static void
875 act_dump_config() {
876     dump_config();
877 }
878
879 static void
880 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
881     (void) page; (void) result;
882
883     if (argv_idx(argv, 0)) {
884         if (strcmp (argv_idx(argv, 0), "0") == 0) {
885             uzbl.behave.insert_mode = FALSE;
886         } else {
887             uzbl.behave.insert_mode = TRUE;
888         }
889     } else {
890         uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
891     }
892
893     update_title();
894 }
895
896 static void
897 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
898     (void) result;
899
900     if (argv_idx(argv, 0)) {
901         GString* newuri = g_string_new (argv_idx(argv, 0));
902         if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
903             run_js(web_view, argv, NULL);
904             return;
905         }
906         if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
907             g_string_prepend (newuri, "http://");
908         /* if we do handle cookies, ask our handler for them */
909         webkit_web_view_load_uri (web_view, newuri->str);
910         g_string_free (newuri, TRUE);
911     }
912 }
913
914
915 /* Javascript*/
916
917 static JSValueRef
918 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
919                 size_t argumentCount, const JSValueRef arguments[],
920                 JSValueRef* exception) {
921     (void) function;
922     (void) thisObject;
923     (void) exception;
924     
925     JSStringRef js_result_string;
926     GString *result = g_string_new("");
927
928     if (argumentCount >= 1) {
929         JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
930         size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
931         char ctl_line[arg_size];
932         JSStringGetUTF8CString(arg, ctl_line, arg_size);
933
934         parse_cmd_line(ctl_line, result);
935
936         JSStringRelease(arg);
937     }
938     js_result_string = JSStringCreateWithUTF8CString(result->str);
939
940     g_string_free(result, TRUE);
941
942     return JSValueMakeString(ctx, js_result_string);
943 }
944
945 static JSStaticFunction js_static_functions[] = {
946     {"run", js_run_command, kJSPropertyAttributeNone},
947 };
948
949 static void
950 js_init() {
951     /* This function creates the class and its definition, only once */
952     if (!uzbl.js.initialized) {
953         /* it would be pretty cool to make this dynamic */
954         uzbl.js.classdef = kJSClassDefinitionEmpty;
955         uzbl.js.classdef.staticFunctions = js_static_functions;
956
957         uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
958     }
959 }
960
961
962 static void 
963 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
964     WebKitWebFrame *frame;
965     JSGlobalContextRef context;
966     JSObjectRef globalobject;
967     JSStringRef var_name;
968
969     JSStringRef js_script;
970     JSValueRef js_result;
971     JSStringRef js_result_string;
972     size_t js_result_size;
973     
974     js_init();
975
976     frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
977     context = webkit_web_frame_get_global_context(frame);
978     globalobject = JSContextGetGlobalObject(context);
979     
980     /* uzbl javascript namespace */
981     var_name = JSStringCreateWithUTF8CString("Uzbl");
982     JSObjectSetProperty(context, globalobject, var_name,
983                         JSObjectMake(context, uzbl.js.classref, NULL),  
984                         kJSClassAttributeNone, NULL);
985     
986     /* evaluate the script and get return value*/ 
987     js_script = JSStringCreateWithUTF8CString(script);
988     js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
989     if (js_result && !JSValueIsUndefined(context, js_result)) {
990         js_result_string = JSValueToStringCopy(context, js_result, NULL);
991         js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
992
993         if (js_result_size) {
994             char js_result_utf8[js_result_size];
995             JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
996             g_string_assign(result, js_result_utf8);
997         }
998
999         JSStringRelease(js_result_string);
1000     }
1001
1002     /* cleanup */
1003     JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1004
1005     JSStringRelease(var_name);
1006     JSStringRelease(js_script);
1007 }
1008
1009 static void
1010 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1011
1012     if (argv_idx(argv, 0))
1013         eval_js(web_view, argv_idx(argv, 0), result);
1014 }
1015
1016 static void
1017 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1018     (void) result;
1019     if (argv_idx(argv, 0)) {
1020         GArray* lines = read_file_by_line (argv_idx (argv, 0));
1021         gchar*  js = NULL;
1022         int i = 0;
1023         gchar* line;
1024
1025         while ((line = g_array_index(lines, gchar*, i))) {
1026             if (js == NULL) {
1027                 js = g_strdup (line);
1028             } else {
1029                 gchar* newjs = g_strconcat (js, line, NULL);
1030                 js = newjs;
1031             }
1032             i ++;
1033             g_free (line);
1034         }
1035         
1036         if (uzbl.state.verbose)
1037             printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1038
1039         if (argv_idx (argv, 1)) {
1040             gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1041             g_free (js);
1042             js = newjs;
1043         }
1044         eval_js (web_view, js, result);
1045         g_free (js);
1046         g_array_free (lines, TRUE);
1047     }
1048 }
1049
1050 static void
1051 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1052     if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1053         if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1054             webkit_web_view_unmark_text_matches (page);
1055             webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1056             uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1057         }
1058     }
1059     
1060     if (uzbl.state.searchtx) {
1061         if (uzbl.state.verbose)
1062             printf ("Searching: %s\n", uzbl.state.searchtx);
1063         webkit_web_view_set_highlight_text_matches (page, TRUE);
1064         webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1065     }
1066 }
1067
1068 static void
1069 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1070     (void) result;
1071     search_text(page, argv, TRUE);
1072 }
1073
1074 static void
1075 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1076     (void) result;
1077     search_text(page, argv, FALSE);
1078 }
1079
1080 static void
1081 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1082     (void) argv; (void) result;
1083     webkit_web_view_set_highlight_text_matches (page, FALSE);
1084 }
1085
1086
1087 static void
1088 new_window_load_uri (const gchar * uri) {
1089     GString* to_execute = g_string_new ("");
1090     g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1091     int i;
1092     for (i = 0; entries[i].long_name != NULL; i++) {
1093         if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1094             gchar** str = (gchar**)entries[i].arg_data;
1095             if (*str!=NULL) {
1096                 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1097             }
1098         }
1099     }
1100     if (uzbl.state.verbose)
1101         printf("\n%s\n", to_execute->str);
1102     g_spawn_command_line_async (to_execute->str, NULL);
1103     g_string_free (to_execute, TRUE);
1104 }
1105
1106 static void
1107 chain (WebKitWebView *page, GArray *argv, GString *result) {
1108     (void) page; (void) result;
1109     gchar *a = NULL;
1110     gchar **parts = NULL;
1111     guint i = 0;    
1112     while ((a = argv_idx(argv, i++))) {
1113         parts = g_strsplit (a, " ", 2);
1114         parse_command(parts[0], parts[1], result);
1115         g_strfreev (parts);
1116     }
1117 }
1118
1119 static void
1120 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1121     (void)page;
1122     (void)argv;
1123     (void)result;
1124     g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1125     run_keycmd(FALSE);
1126     update_title();
1127 }
1128
1129 static void
1130 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1131     (void)page;
1132     (void)argv;
1133     (void)result;
1134     g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1135     run_keycmd(TRUE);
1136     update_title();
1137 }
1138
1139 static void
1140 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1141     gchar *prev;
1142     (void)page;
1143     (void)argv;
1144     (void)result;
1145     prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
1146     if (prev)
1147       g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1148     update_title();
1149 }
1150
1151 static void
1152 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1153     (void)page;
1154     (void)argv;
1155     (void)result;
1156     gtk_main_quit ();
1157 }
1158
1159 /* --Statusbar functions-- */
1160 static char*
1161 build_progressbar_ascii(int percent) {
1162    int width=uzbl.gui.sbar.progress_w;
1163    int i;
1164    double l;
1165    GString *bar = g_string_new("");
1166
1167    l = (double)percent*((double)width/100.);
1168    l = (int)(l+.5)>=(int)l ? l+.5 : l;
1169
1170    for(i=0; i<(int)l; i++)
1171        g_string_append(bar, uzbl.gui.sbar.progress_s);
1172
1173    for(; i<width; i++)
1174        g_string_append(bar, uzbl.gui.sbar.progress_u);
1175
1176    return g_string_free(bar, FALSE);
1177 }
1178
1179 void
1180 setup_scanner() {
1181      const GScannerConfig scan_config = {
1182              (
1183               "\t\r\n"
1184              )            /* cset_skip_characters */,
1185              (
1186               G_CSET_a_2_z
1187               "_#"
1188               G_CSET_A_2_Z
1189              )            /* cset_identifier_first */,
1190              (
1191               G_CSET_a_2_z
1192               "_0123456789"
1193               G_CSET_A_2_Z
1194               G_CSET_LATINS
1195               G_CSET_LATINC
1196              )            /* cset_identifier_nth */,
1197              ( "" )    /* cpair_comment_single */,
1198
1199              TRUE         /* case_sensitive */,
1200
1201              FALSE        /* skip_comment_multi */,
1202              FALSE        /* skip_comment_single */,
1203              FALSE        /* scan_comment_multi */,
1204              TRUE         /* scan_identifier */,
1205              TRUE         /* scan_identifier_1char */,
1206              FALSE        /* scan_identifier_NULL */,
1207              TRUE         /* scan_symbols */,
1208              FALSE        /* scan_binary */,
1209              FALSE        /* scan_octal */,
1210              FALSE        /* scan_float */,
1211              FALSE        /* scan_hex */,
1212              FALSE        /* scan_hex_dollar */,
1213              FALSE        /* scan_string_sq */,
1214              FALSE        /* scan_string_dq */,
1215              TRUE         /* numbers_2_int */,
1216              FALSE        /* int_2_float */,
1217              FALSE        /* identifier_2_string */,
1218              FALSE        /* char_2_token */,
1219              FALSE        /* symbol_2_token */,
1220              TRUE         /* scope_0_fallback */,
1221              FALSE,
1222              TRUE
1223      };
1224
1225      uzbl.scan = g_scanner_new(&scan_config);
1226      while(symp->symbol_name) {
1227          g_scanner_scope_add_symbol(uzbl.scan, 0,
1228                          symp->symbol_name,
1229                          GINT_TO_POINTER(symp->symbol_token));
1230          symp++;
1231      }
1232 }
1233
1234 gchar *
1235 expand_template(const char *template, gboolean escape_markup) {
1236      if(!template) return NULL;
1237
1238      GTokenType token = G_TOKEN_NONE;
1239      GString *ret = g_string_new("");
1240      char *buf=NULL;
1241      int sym;
1242
1243      g_scanner_input_text(uzbl.scan, template, strlen(template));
1244      while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1245          token = g_scanner_get_next_token(uzbl.scan);
1246
1247          if(token == G_TOKEN_SYMBOL) {
1248              sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1249              switch(sym) {
1250                  case SYM_URI:
1251                      if(escape_markup) {
1252                          buf = uzbl.state.uri?
1253                              g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
1254                          g_string_append(ret, buf);
1255                          g_free(buf);
1256                      }
1257                      else
1258                          g_string_append(ret, uzbl.state.uri?
1259                                  uzbl.state.uri:g_strdup(""));
1260                      break;
1261                  case SYM_LOADPRGS:
1262                      buf = itos(uzbl.gui.sbar.load_progress);
1263                      g_string_append(ret, buf);
1264                      g_free(buf);
1265                      break;
1266                  case SYM_LOADPRGSBAR:
1267                      buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1268                      g_string_append(ret, buf);
1269                      g_free(buf);
1270                      break;
1271                  case SYM_TITLE:
1272                      if(escape_markup) {
1273                          buf = uzbl.gui.main_title?
1274                              g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
1275                          g_string_append(ret, buf);
1276                          g_free(buf);
1277                      }
1278                      else
1279                          g_string_append(ret, uzbl.gui.main_title?
1280                                  uzbl.gui.main_title:g_strdup(""));
1281                      break;
1282                  case SYM_SELECTED_URI:
1283                      if(escape_markup) {
1284                          buf = uzbl.state.selected_url?
1285                              g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
1286                          g_string_append(ret, buf);
1287                          g_free(buf);
1288                      }
1289                      else
1290                          g_string_append(ret, uzbl.state.selected_url?
1291                                  uzbl.state.selected_url:g_strdup(""));
1292                      break;
1293                  case SYM_NAME:
1294                      buf = itos(uzbl.xwin);
1295                      g_string_append(ret,
1296                              uzbl.state.instance_name?uzbl.state.instance_name:buf);
1297                      g_free(buf);
1298                      break;
1299                  case SYM_KEYCMD:
1300                      if(escape_markup) {
1301                          buf = uzbl.state.keycmd->str?
1302                              g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1303                          g_string_append(ret, buf);
1304                          g_free(buf);
1305                      }
1306                      else
1307                          g_string_append(ret, uzbl.state.keycmd->str?
1308                                  uzbl.state.keycmd->str:g_strdup(""));
1309                      break;
1310                  case SYM_MODE:
1311                      g_string_append(ret,
1312                              uzbl.behave.insert_mode?
1313                              uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1314                      break;
1315                  case SYM_MSG:
1316                      g_string_append(ret,
1317                              uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1318                      break;
1319                      /* useragent syms */
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 void
1760 cmd_useragent() {
1761     if (*uzbl.net.useragent == ' ') {
1762         g_free (uzbl.net.useragent);
1763         uzbl.net.useragent = NULL;
1764     } else {
1765         g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1766             uzbl.net.useragent, NULL);
1767     }
1768 }
1769
1770 static void
1771 move_statusbar() {
1772     gtk_widget_ref(uzbl.gui.scrolled_win);
1773     gtk_widget_ref(uzbl.gui.mainbar);
1774     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1775     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1776
1777     if(uzbl.behave.status_top) {
1778         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1779         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1780     }
1781     else {
1782         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1783         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1784     }
1785     gtk_widget_unref(uzbl.gui.scrolled_win);
1786     gtk_widget_unref(uzbl.gui.mainbar);
1787     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1788     return;
1789 }
1790
1791 gboolean
1792 set_var_value(gchar *name, gchar *val) {
1793     uzbl_cmdprop *c = NULL;
1794     char *endp = NULL;
1795     char *buf = NULL;
1796
1797     if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1798         /* check for the variable type */
1799         if (c->type == TYPE_STR) {
1800             buf = expand(val, 0);
1801             g_free(*c->ptr);
1802             *c->ptr = buf;
1803         } else if(c->type == TYPE_INT) {
1804             int *ip = (int *)c->ptr;
1805             buf = expand(val, 0);
1806             *ip = (int)strtoul(buf, &endp, 10);
1807             g_free(buf);
1808         } else if (c->type == TYPE_FLOAT) {
1809             float *fp = (float *)c->ptr;
1810             buf = expand(val, 0);
1811             *fp = strtod(buf, &endp);
1812             g_free(buf);
1813         }
1814
1815         /* invoke a command specific function */
1816         if(c->func) c->func();
1817     }
1818     return TRUE;
1819 }
1820
1821 static void
1822 render_html() {
1823     Behaviour *b = &uzbl.behave;
1824
1825     if(b->html_buffer->str) {
1826         webkit_web_view_load_html_string (uzbl.gui.web_view,
1827                 b->html_buffer->str, b->base_url);
1828         g_string_free(b->html_buffer, TRUE);
1829         b->html_buffer = g_string_new("");
1830     }
1831 }
1832
1833 enum {M_CMD, M_HTML};
1834 static void
1835 parse_cmd_line(const char *ctl_line, GString *result) {
1836     Behaviour *b = &uzbl.behave;
1837     size_t len=0;
1838
1839     if(b->mode == M_HTML) {
1840         len = strlen(b->html_endmarker);
1841         /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1842         if(len == strlen(ctl_line)-1 &&
1843            !strncmp(b->html_endmarker, ctl_line, len)) {
1844             set_timeout(0);
1845             set_var_value("mode", "0");
1846             render_html();
1847             return;
1848         }
1849         else {
1850             set_timeout(b->html_timeout);
1851             g_string_append(b->html_buffer, ctl_line);
1852         }
1853     }
1854     else if((ctl_line[0] == '#') /* Comments */
1855             || (ctl_line[0] == ' ')
1856             || (ctl_line[0] == '\n'))
1857         ; /* ignore these lines */
1858     else { /* parse a command */
1859         gchar *ctlstrip;
1860         gchar **tokens = NULL;
1861         len = strlen(ctl_line);
1862
1863         if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1864             ctlstrip = g_strndup(ctl_line, len - 1);
1865         else ctlstrip = g_strdup(ctl_line);
1866
1867         tokens = g_strsplit(ctlstrip, " ", 2);
1868         parse_command(tokens[0], tokens[1], result);
1869         g_free(ctlstrip);
1870         g_strfreev(tokens);
1871     }
1872 }
1873
1874 static gchar*
1875 build_stream_name(int type, const gchar* dir) {
1876     char *xwin_str = NULL;
1877     State *s = &uzbl.state;
1878     gchar *str = NULL;
1879
1880     xwin_str = itos((int)uzbl.xwin);
1881     if (type == FIFO) {
1882         str = g_strdup_printf
1883             ("%s/uzbl_fifo_%s", dir,
1884              s->instance_name ? s->instance_name : xwin_str);
1885     } else if (type == SOCKET) {
1886         str = g_strdup_printf
1887             ("%s/uzbl_socket_%s", dir,
1888              s->instance_name ? s->instance_name : xwin_str );
1889     }
1890     g_free(xwin_str);
1891     return str;
1892 }
1893
1894 static gboolean
1895 control_fifo(GIOChannel *gio, GIOCondition condition) {
1896     if (uzbl.state.verbose)
1897         printf("triggered\n");
1898     gchar *ctl_line;
1899     GIOStatus ret;
1900     GError *err = NULL;
1901
1902     if (condition & G_IO_HUP)
1903         g_error ("Fifo: Read end of pipe died!\n");
1904
1905     if(!gio)
1906        g_error ("Fifo: GIOChannel broke\n");
1907
1908     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1909     if (ret == G_IO_STATUS_ERROR) {
1910         g_error ("Fifo: Error reading: %s\n", err->message);
1911         g_error_free (err);
1912     }
1913
1914     parse_cmd_line(ctl_line, NULL);
1915     g_free(ctl_line);
1916
1917     return TRUE;
1918 }
1919
1920 static gchar*
1921 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1922     if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1923         if (unlink(uzbl.comm.fifo_path) == -1)
1924             g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1925         g_free(uzbl.comm.fifo_path);
1926         uzbl.comm.fifo_path = NULL;
1927     }
1928
1929     if (*dir == ' ') { /* space unsets the variable */
1930         g_free (dir);
1931         return NULL;
1932     }
1933
1934     GIOChannel *chan = NULL;
1935     GError *error = NULL;
1936     gchar *path = build_stream_name(FIFO, dir);
1937
1938     if (!file_exists(path)) {
1939         if (mkfifo (path, 0666) == 0) {
1940             // 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.
1941             chan = g_io_channel_new_file(path, "r+", &error);
1942             if (chan) {
1943                 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1944                     if (uzbl.state.verbose)
1945                         printf ("init_fifo: created successfully as %s\n", path);
1946                     uzbl.comm.fifo_path = path;
1947                     return dir;
1948                 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1949             } else g_warning ("init_fifo: can't open: %s\n", error->message);
1950         } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1951     } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1952
1953     /* if we got this far, there was an error; cleanup */
1954     if (error) g_error_free (error);
1955     g_free(dir);
1956     g_free(path);
1957     return NULL;
1958 }
1959
1960 static gboolean
1961 control_stdin(GIOChannel *gio, GIOCondition condition) {
1962     (void) condition;
1963     gchar *ctl_line = NULL;
1964     GIOStatus ret;
1965
1966     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1967     if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1968         return FALSE;
1969
1970     parse_cmd_line(ctl_line, NULL);
1971     g_free(ctl_line);
1972
1973     return TRUE;
1974 }
1975
1976 static void
1977 create_stdin () {
1978     GIOChannel *chan = NULL;
1979     GError *error = NULL;
1980
1981     chan = g_io_channel_unix_new(fileno(stdin));
1982     if (chan) {
1983         if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1984             g_error ("Stdin: could not add watch\n");
1985         } else {
1986             if (uzbl.state.verbose)
1987                 printf ("Stdin: watch added successfully\n");
1988         }
1989     } else {
1990         g_error ("Stdin: Error while opening: %s\n", error->message);
1991     }
1992     if (error) g_error_free (error);
1993 }
1994
1995 static gboolean
1996 control_socket(GIOChannel *chan) {
1997     struct sockaddr_un remote;
1998     unsigned int t = sizeof(remote);
1999     int clientsock;
2000     GIOChannel *clientchan;
2001
2002     clientsock = accept (g_io_channel_unix_get_fd(chan),
2003                          (struct sockaddr *) &remote, &t);
2004     
2005     if ((clientchan = g_io_channel_unix_new(clientsock))) {
2006         g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2007                        (GIOFunc) control_client_socket, clientchan);
2008     }
2009
2010     return TRUE;
2011 }
2012
2013 static gboolean
2014 control_client_socket(GIOChannel *clientchan) {
2015     char *ctl_line;
2016     GString *result = g_string_new("");
2017     GError *error = NULL;
2018     GIOStatus ret;
2019     gsize len;
2020
2021     ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2022     if (ret == G_IO_STATUS_ERROR) {
2023         g_warning ("Error reading: %s\n", error->message);
2024         g_io_channel_shutdown(clientchan, TRUE, &error);
2025         return FALSE;
2026     } else if (ret == G_IO_STATUS_EOF) {
2027         /* shutdown and remove channel watch from main loop */
2028         g_io_channel_shutdown(clientchan, TRUE, &error);
2029         return FALSE;
2030     }
2031
2032     if (ctl_line) {
2033         parse_cmd_line (ctl_line, result);
2034         g_string_append_c(result, '\n');
2035         ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2036                                         &len, &error);
2037         if (ret == G_IO_STATUS_ERROR) {
2038             g_warning ("Error writing: %s", error->message);
2039         }
2040         g_io_channel_flush(clientchan, &error);
2041     }
2042
2043     if (error) g_error_free (error);
2044     g_string_free(result, TRUE);
2045     g_free(ctl_line);
2046     return TRUE;
2047 }
2048
2049 static gchar*
2050 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2051     if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2052         if (unlink(uzbl.comm.socket_path) == -1)
2053             g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2054         g_free(uzbl.comm.socket_path);
2055         uzbl.comm.socket_path = NULL;
2056     }
2057
2058     if (*dir == ' ') {
2059         g_free(dir);
2060         return NULL;
2061     }
2062
2063     GIOChannel *chan = NULL;
2064     int sock, len;
2065     struct sockaddr_un local;
2066     gchar *path = build_stream_name(SOCKET, dir);
2067
2068     sock = socket (AF_UNIX, SOCK_STREAM, 0);
2069
2070     local.sun_family = AF_UNIX;
2071     strcpy (local.sun_path, path);
2072     unlink (local.sun_path);
2073
2074     len = strlen (local.sun_path) + sizeof (local.sun_family);
2075     if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2076         if (uzbl.state.verbose)
2077             printf ("init_socket: opened in %s\n", path);
2078         listen (sock, 5);
2079
2080         if( (chan = g_io_channel_unix_new(sock)) ) {
2081             g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2082             uzbl.comm.socket_path = path;
2083             return dir;
2084         }
2085     } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2086
2087     /* if we got this far, there was an error; cleanup */
2088     g_free(path);
2089     g_free(dir);
2090     return NULL;
2091 }
2092
2093 /*
2094  NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2095  it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2096 */
2097 // this function may be called very early when the templates are not set (yet), hence the checks
2098 static void
2099 update_title (void) {
2100     Behaviour *b = &uzbl.behave;
2101     gchar *parsed;
2102
2103     if (b->show_status) {
2104         if (b->title_format_short) {
2105             parsed = expand_template(b->title_format_short, FALSE);
2106             if (uzbl.gui.main_window)
2107                 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2108             g_free(parsed);
2109         }
2110         if (b->status_format) {
2111             parsed = expand_template(b->status_format, TRUE);
2112             gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2113             g_free(parsed);
2114         }
2115         if (b->status_background) {
2116             GdkColor color;
2117             gdk_color_parse (b->status_background, &color);
2118             //labels and hboxes do not draw their own background. applying this on the window/vbox is ok as the statusbar is the only affected widget. (if not, we could also use GtkEventBox)
2119             if (uzbl.gui.main_window)
2120                     gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2121             else if (uzbl.gui.plug)
2122                     gtk_widget_modify_bg ((GtkWidget * ) uzbl.gui.plug, 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), "notify::title", G_CALLBACK (title_change_cb), NULL);
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 (g_strcmp0(s->config_file, "-") == 0) {
2495         s->config_file = NULL;
2496         create_stdin();
2497     }
2498
2499     else if (!s->config_file) {
2500         s->config_file = find_xdg_file (0, "/uzbl/config");
2501     }
2502
2503     if (s->config_file) {
2504         GArray* lines = read_file_by_line (s->config_file);
2505         int i = 0;
2506         gchar* line;
2507
2508         while ((line = g_array_index(lines, gchar*, i))) {
2509             parse_cmd_line (line, NULL);
2510             i ++;
2511             g_free (line);
2512         }
2513         g_array_free (lines, TRUE);
2514     } else {
2515         if (uzbl.state.verbose)
2516             printf ("No configuration file loaded.\n");
2517     }
2518
2519     g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2520 }
2521
2522 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2523     (void) session;
2524     (void) user_data;
2525     if (!uzbl.behave.cookie_handler)
2526          return;
2527
2528     soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2529     GString *s = g_string_new ("");
2530     SoupURI * soup_uri = soup_message_get_uri(msg);
2531     g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2532     run_handler(uzbl.behave.cookie_handler, s->str);
2533
2534     if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2535         char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2536         if ( p != NULL ) *p = '\0';
2537         soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2538     }
2539     if (uzbl.comm.sync_stdout)
2540         uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2541         
2542     g_string_free(s, TRUE);
2543 }
2544
2545 static void
2546 save_cookies (SoupMessage *msg, gpointer user_data){
2547     (void) user_data;
2548     GSList *ck;
2549     char *cookie;
2550     for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2551         cookie = soup_cookie_to_set_cookie_header(ck->data);
2552         SoupURI * soup_uri = soup_message_get_uri(msg);
2553         GString *s = g_string_new ("");
2554         g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2555         run_handler(uzbl.behave.cookie_handler, s->str);
2556         g_free (cookie);
2557         g_string_free(s, TRUE);
2558     }
2559     g_slist_free(ck);
2560 }
2561
2562 /* --- WEBINSPECTOR --- */
2563 static void
2564 hide_window_cb(GtkWidget *widget, gpointer data) {
2565     (void) data;
2566
2567     gtk_widget_hide(widget);
2568 }
2569
2570 static WebKitWebView*
2571 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2572     (void) data;
2573     (void) page;
2574     (void) web_inspector;
2575     GtkWidget* scrolled_window;
2576     GtkWidget* new_web_view;
2577     GUI *g = &uzbl.gui;
2578
2579     g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2580     g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2581             G_CALLBACK(hide_window_cb), NULL);
2582
2583     gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2584     gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2585     gtk_widget_show(g->inspector_window);
2586
2587     scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2588     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2589             GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2590     gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2591     gtk_widget_show(scrolled_window);
2592
2593     new_web_view = webkit_web_view_new();
2594     gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2595
2596     return WEBKIT_WEB_VIEW(new_web_view);
2597 }
2598
2599 static gboolean
2600 inspector_show_window_cb (WebKitWebInspector* inspector){
2601     (void) inspector;
2602     gtk_widget_show(uzbl.gui.inspector_window);
2603     return TRUE;
2604 }
2605
2606 /* TODO: Add variables and code to make use of these functions */
2607 static gboolean
2608 inspector_close_window_cb (WebKitWebInspector* inspector){
2609     (void) inspector;
2610     return TRUE;
2611 }
2612
2613 static gboolean
2614 inspector_attach_window_cb (WebKitWebInspector* inspector){
2615     (void) inspector;
2616     return FALSE;
2617 }
2618
2619 static gboolean
2620 inspector_detach_window_cb (WebKitWebInspector* inspector){
2621     (void) inspector;
2622     return FALSE;
2623 }
2624
2625 static gboolean
2626 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2627     (void) inspector;
2628     return FALSE;
2629 }
2630
2631 static gboolean
2632 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2633     (void) inspector;
2634     return FALSE;
2635 }
2636
2637 static void
2638 set_up_inspector() {
2639     GUI *g = &uzbl.gui;
2640     WebKitWebSettings *settings = view_settings();
2641     g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2642
2643     uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2644     g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2645     g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2646     g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2647     g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2648     g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2649     g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2650
2651     g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2652 }
2653
2654 static void
2655 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2656     (void) ud;
2657     uzbl_cmdprop *c = v;
2658
2659     if(!c->dump)
2660         return;
2661
2662     if(c->type == TYPE_STR)
2663         printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2664     else if(c->type == TYPE_INT)
2665         printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2666 }
2667
2668 static void
2669 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2670     (void) ud;
2671     Action *a = v;
2672
2673     printf("bind %s = %s %s\n", (char *)k ,
2674             (char *)a->name, a->param?(char *)a->param:"");
2675 }
2676
2677 static void
2678 dump_config() {
2679     g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2680     g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2681 }
2682
2683 #ifndef UZBL_LIBRARY
2684 /** -- MAIN -- **/
2685 int
2686 main (int argc, char* argv[]) {
2687     gtk_init (&argc, &argv);
2688     if (!g_thread_supported ())
2689         g_thread_init (NULL);
2690     uzbl.state.executable_path = g_strdup(argv[0]);
2691     uzbl.state.selected_url = NULL;
2692     uzbl.state.searchtx = NULL;
2693
2694     GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2695     g_option_context_add_main_entries (context, entries, NULL);
2696     g_option_context_add_group (context, gtk_get_option_group (TRUE));
2697     g_option_context_parse (context, &argc, &argv, NULL);
2698     g_option_context_free(context);
2699     
2700     gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2701     gboolean verbose_override = uzbl.state.verbose;
2702
2703     /* initialize hash table */
2704     uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2705
2706     uzbl.net.soup_session = webkit_get_default_session();
2707     uzbl.state.keycmd = g_string_new("");
2708
2709     if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2710         fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2711     if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2712         fprintf(stderr, "uzbl: error hooking SIGINT\n");
2713     if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2714         fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2715
2716
2717     if(uname(&uzbl.state.unameinfo) == -1)
2718         g_printerr("Can't retrieve unameinfo.  Your useragent might appear wrong.\n");
2719
2720     uzbl.gui.sbar.progress_s = g_strdup("=");
2721     uzbl.gui.sbar.progress_u = g_strdup("ยท");
2722     uzbl.gui.sbar.progress_w = 10;
2723
2724     /* HTML mode defaults*/
2725     uzbl.behave.html_buffer = g_string_new("");
2726     uzbl.behave.html_endmarker = g_strdup(".");
2727     uzbl.behave.html_timeout = 60;
2728     uzbl.behave.base_url = g_strdup("http://invalid");
2729
2730     /* default mode indicators */
2731     uzbl.behave.insert_indicator = g_strdup("I");
2732     uzbl.behave.cmd_indicator    = g_strdup("C");
2733
2734     setup_scanner();
2735     commands_hash ();
2736     make_var_to_name_hash();
2737
2738     uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2739
2740     uzbl.gui.scrolled_win = create_browser();
2741     create_mainbar();
2742
2743     /* initial packing */
2744     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2745     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2746
2747     if (uzbl.state.socket_id) {
2748         uzbl.gui.plug = create_plug ();
2749         gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2750         gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2751     } else {
2752         uzbl.gui.main_window = create_window ();
2753         gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2754         gtk_widget_show_all (uzbl.gui.main_window);
2755         uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2756     }
2757
2758     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2759
2760     if (uzbl.state.verbose) {
2761         printf("Uzbl start location: %s\n", argv[0]);
2762         if (uzbl.state.socket_id)
2763             printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2764         else
2765             printf("window_id %i\n",(int) uzbl.xwin);
2766         printf("pid %i\n", getpid ());
2767         printf("name: %s\n", uzbl.state.instance_name);
2768     }
2769
2770     uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2771     uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2772     uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2773     uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2774     gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2775
2776     settings_init ();
2777
2778     if (!uzbl.behave.show_status)
2779         gtk_widget_hide(uzbl.gui.mainbar);
2780     else
2781         update_title();
2782
2783     /* WebInspector */
2784     set_up_inspector();
2785
2786     if (verbose_override > uzbl.state.verbose)
2787         uzbl.state.verbose = verbose_override;
2788     
2789     if (uri_override) {
2790         set_var_value("uri", uri_override);
2791         g_free(uri_override);
2792     } else if (uzbl.state.uri)
2793         cmd_load_uri(uzbl.gui.web_view, NULL);
2794
2795     gtk_main ();
2796     clean_up();
2797
2798     return EXIT_SUCCESS;
2799 }
2800 #endif
2801
2802 /* vi: set et ts=4: */