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