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