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