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