added support for WebInspector
[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
36 #include <gtk/gtk.h>
37 #include <gdk/gdkx.h>
38 #include <gdk/gdkkeysyms.h>
39 #include <sys/socket.h>
40 #include <sys/stat.h>
41 #include <sys/types.h>
42 #include <sys/un.h>
43 #include <sys/utsname.h>
44 #include <webkit/webkit.h>
45 #include <stdio.h>
46 #include <string.h>
47 #include <unistd.h>
48 #include <stdlib.h>
49 #include <errno.h>
50 #include <string.h>
51 #include <fcntl.h>
52 #include <sys/socket.h>
53 #include <sys/un.h>
54 #include <libsoup/soup.h>
55 #include <signal.h>
56 #include "uzbl.h"
57 #include "config.h"
58
59 static Uzbl uzbl;
60 typedef void (*Command)(WebKitWebView*, GArray *argv);
61
62 /* commandline arguments (set initial values for the state variables) */
63 static GOptionEntry entries[] =
64 {
65     { "uri",     'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,           "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
66     { "verbose", 'v', 0, G_OPTION_ARG_NONE,   &uzbl.state.verbose,       "Whether to print all messages or just errors.", NULL },
67     { "name",    'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name, "Name of the current instance (defaults to Xorg window id)", "NAME" },
68     { "config",  'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,   "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
69     { NULL,      0, 0, 0, NULL, NULL, NULL }
70 };
71
72
73 /* associate command names to their properties */
74 typedef const struct {
75     void **ptr;
76     int type;
77     void (*func)(void);
78 } uzbl_cmdprop;
79
80 enum {TYPE_INT, TYPE_STR};
81
82 /* an abbreviation to help keep the table's width humane */
83 #define PTR(var, t, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .func = fun }
84
85 const struct {
86     char *name;
87     uzbl_cmdprop cp;
88 } var_name_to_ptr[] = {
89 /*    variable name         pointer to variable in code          type  callback function    */
90 /*  --------------------------------------------------------------------------------------- */
91     { "uri",                 PTR(uzbl.state.uri,                  STR, cmd_load_uri)},
92     { "status_message",      PTR(uzbl.gui.sbar.msg,               STR, update_title)},
93     { "show_status",         PTR(uzbl.behave.show_status,         INT, cmd_set_status)},
94     { "status_top",          PTR(uzbl.behave.status_top,          INT, move_statusbar)},
95     { "status_format",       PTR(uzbl.behave.status_format,       STR, update_title)},
96     { "status_pbar_done",    PTR(uzbl.gui.sbar.progress_s,        STR, update_title)},
97     { "status_pbar_pending", PTR(uzbl.gui.sbar.progress_u,        STR, update_title)},
98     { "status_pbar_width",   PTR(uzbl.gui.sbar.progress_w,        INT, update_title)},
99     { "status_background",   PTR(uzbl.behave.status_background,   STR, update_title)},
100     { "title_format_long",   PTR(uzbl.behave.title_format_long,   STR, update_title)},
101     { "title_format_short",  PTR(uzbl.behave.title_format_short,  STR, update_title)},
102     { "insert_mode",         PTR(uzbl.behave.insert_mode,         INT, NULL)},
103     { "always_insert_mode",  PTR(uzbl.behave.always_insert_mode,  INT, cmd_always_insert_mode)},
104     { "reset_command_mode",  PTR(uzbl.behave.reset_command_mode,  INT, NULL)},
105     { "modkey",              PTR(uzbl.behave.modkey,              STR, cmd_modkey)},
106     { "load_finish_handler", PTR(uzbl.behave.load_finish_handler, STR, NULL)},
107     { "load_start_handler",  PTR(uzbl.behave.load_start_handler,  STR, NULL)},
108     { "load_commit_handler", PTR(uzbl.behave.load_commit_handler, STR, NULL)},
109     { "history_handler",     PTR(uzbl.behave.history_handler,     STR, NULL)},
110     { "download_handler",    PTR(uzbl.behave.download_handler,    STR, NULL)},
111     { "cookie_handler",      PTR(uzbl.behave.cookie_handler,      STR, cmd_cookie_handler)},
112     { "fifo_dir",            PTR(uzbl.behave.fifo_dir,            STR, cmd_fifo_dir)},
113     { "socket_dir",          PTR(uzbl.behave.socket_dir,          STR, cmd_socket_dir)},
114     { "http_debug",          PTR(uzbl.behave.http_debug,          INT, cmd_http_debug)},
115     { "shell_cmd",           PTR(uzbl.behave.shell_cmd,           STR, NULL)},
116     { "proxy_url",           PTR(uzbl.net.proxy_url,              STR, set_proxy_url)},
117     { "max_conns",           PTR(uzbl.net.max_conns,              INT, cmd_max_conns)},
118     { "max_conns_host",      PTR(uzbl.net.max_conns_host,         INT, cmd_max_conns_host)},
119     { "useragent",           PTR(uzbl.net.useragent,              STR, cmd_useragent)},
120     /* exported WebKitWebSettings properties*/
121     { "font_size",           PTR(uzbl.behave.font_size,           INT, cmd_font_size)},
122     { "monospace_size",      PTR(uzbl.behave.monospace_size,      INT, cmd_font_size)},
123     { "minimum_font_size",   PTR(uzbl.behave.minimum_font_size,   INT, cmd_minimum_font_size)},
124     { "disable_plugins",     PTR(uzbl.behave.disable_plugins,     INT, cmd_disable_plugins)},
125     { "disable_scripts",     PTR(uzbl.behave.disable_scripts,     INT, cmd_disable_scripts)},
126     { "autoload_images",     PTR(uzbl.behave.autoload_img,        INT, cmd_autoload_img)},
127     { "autoshrink_images",   PTR(uzbl.behave.autoshrink_img,      INT, cmd_autoshrink_img)},
128     { "enable_spellcheck",   PTR(uzbl.behave.enable_spellcheck,   INT, cmd_enable_spellcheck)},
129     { "enable_private",      PTR(uzbl.behave.enable_private,      INT, cmd_enable_private)},
130     { "print_backgrounds",   PTR(uzbl.behave.print_bg,            INT, cmd_print_bg)},
131     { "stylesheet_uri",      PTR(uzbl.behave.style_uri,           STR, cmd_style_uri)},
132
133     { NULL,                  {.ptr = NULL, .type = TYPE_INT, .func = NULL}}
134 }, *n2v_p = var_name_to_ptr;
135
136 const struct {
137     char *key;
138     guint mask;
139 } modkeys[] = {
140     { "SHIFT",   GDK_SHIFT_MASK   }, // shift
141     { "LOCK",    GDK_LOCK_MASK    }, // capslock or shiftlock, depending on xserver's modmappings
142     { "CONTROL", GDK_CONTROL_MASK }, // control
143     { "MOD1",    GDK_MOD1_MASK    }, // 4th mod - normally alt but depends on modmappings
144     { "MOD2",    GDK_MOD2_MASK    }, // 5th mod
145     { "MOD3",    GDK_MOD3_MASK    }, // 6th mod
146     { "MOD4",    GDK_MOD4_MASK    }, // 7th mod
147     { "MOD5",    GDK_MOD5_MASK    }, // 8th mod
148     { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
149     { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
150     { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
151     { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
152     { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
153     { "SUPER",   GDK_SUPER_MASK   }, // super (since 2.10)
154     { "HYPER",   GDK_HYPER_MASK   }, // hyper (since 2.10)
155     { "META",    GDK_META_MASK    }, // meta (since 2.10)
156     { NULL,      0                }
157 };
158
159
160 /* construct a hash from the var_name_to_ptr array for quick access */
161 static void
162 make_var_to_name_hash() {
163     uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
164     while(n2v_p->name) {
165         g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
166         n2v_p++;
167     }
168 }
169
170
171 /* --- UTILITY FUNCTIONS --- */
172
173 char *
174 itos(int val) {
175     char tmp[20];
176
177     snprintf(tmp, sizeof(tmp), "%i", val);
178     return g_strdup(tmp);
179 }
180
181 static gchar*
182 strfree(gchar *str) { g_free(str); return NULL; }  // for freeing & setting to null in one go
183
184 static gchar*
185 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
186
187 static char *
188 str_replace (const char* search, const char* replace, const char* string) {
189     gchar **buf;
190     char *ret;
191
192     buf = g_strsplit (string, search, -1);
193     ret = g_strjoinv (replace, buf);
194     g_strfreev(buf); // somebody said this segfaults
195
196     return ret;
197 }
198
199 static GArray*
200 read_file_by_line (gchar *path) {
201     GIOChannel *chan = NULL;
202     gchar *readbuf = NULL;
203     gsize len;
204     GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
205     int i = 0;
206     
207     chan = g_io_channel_new_file(path, "r", NULL);
208     
209     if (chan) {
210         while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
211             const gchar* val = g_strdup (readbuf);
212             g_array_append_val (lines, val);
213             g_free (readbuf);
214             i ++;
215         }
216         
217         g_io_channel_unref (chan);
218     } else {
219         fprintf(stderr, "File '%s' not be read.\n", path);
220     }
221     
222     return lines;
223 }
224
225 static
226 gchar* parseenv (char* string) {
227     extern char** environ;
228     gchar* tmpstr = NULL;
229     int i = 0;
230     
231
232     while (environ[i] != NULL) {
233         gchar** env = g_strsplit (environ[i], "=", 2);
234         gchar* envname = g_strconcat ("$", env[0], NULL);
235
236         if (g_strrstr (string, envname) != NULL) {
237             tmpstr = g_strdup(string);
238             g_free (string);
239             string = str_replace(envname, env[1], tmpstr);
240             g_free (tmpstr);
241         }
242
243         g_free (envname);
244         g_strfreev (env); // somebody said this breaks uzbl
245         i++;
246     }
247
248     return string;
249 }
250
251 static sigfunc*
252 setup_signal(int signr, sigfunc *shandler) {
253     struct sigaction nh, oh;
254
255     nh.sa_handler = shandler;
256     sigemptyset(&nh.sa_mask);
257     nh.sa_flags = 0;
258
259     if(sigaction(signr, &nh, &oh) < 0)
260         return SIG_ERR;
261
262     return NULL;
263 }
264
265 static void
266 clean_up(void) {
267     if (uzbl.behave.fifo_dir)
268         unlink (uzbl.comm.fifo_path);
269     if (uzbl.behave.socket_dir)
270         unlink (uzbl.comm.socket_path);
271
272     g_free(uzbl.state.executable_path);
273     g_string_free(uzbl.state.keycmd, TRUE);
274     g_hash_table_destroy(uzbl.bindings);
275     g_hash_table_destroy(uzbl.behave.commands);
276 }
277
278
279 /* --- SIGNAL HANDLER --- */
280
281 static void
282 catch_sigterm(int s) {
283     (void) s;
284     clean_up();
285 }
286
287 static void
288 catch_sigint(int s) {
289     (void) s;
290     clean_up();
291     exit(EXIT_SUCCESS);
292 }
293
294 /* --- CALLBACKS --- */
295
296 static gboolean
297 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
298     (void) web_view;
299     (void) frame;
300     (void) navigation_action;
301     (void) policy_decision;
302     (void) user_data;
303     const gchar* uri = webkit_network_request_get_uri (request);
304     if (uzbl.state.verbose)
305         printf("New window requested -> %s \n", uri);
306     new_window_load_uri(uri);
307     return (FALSE);
308 }
309
310 WebKitWebView*
311 create_web_view_cb (WebKitWebView  *web_view, WebKitWebFrame *frame, gpointer user_data) {
312     (void) web_view;
313     (void) frame;
314     (void) user_data;
315     if (uzbl.state.selected_url != NULL) {
316         if (uzbl.state.verbose)
317             printf("\nNew web view -> %s\n",uzbl.state.selected_url);
318         new_window_load_uri(uzbl.state.selected_url);
319     } else {
320         if (uzbl.state.verbose)
321             printf("New web view -> %s\n","Nothing to open, exiting");
322     }
323     return (NULL);
324 }
325
326 static gboolean
327 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
328     (void) web_view;
329     (void) user_data;
330     if (uzbl.behave.download_handler) {
331         const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
332         if (uzbl.state.verbose)
333             printf("Download -> %s\n",uri);
334         /* if urls not escaped, we may have to escape and quote uri before this call */
335         run_handler(uzbl.behave.download_handler, uri);
336     }
337     return (FALSE);
338 }
339
340 /* scroll a bar in a given direction */
341 static void
342 scroll (GtkAdjustment* bar, GArray *argv) {
343     gdouble amount;
344     gchar *end;
345
346     amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
347     if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
348     gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
349 }
350
351 static void scroll_begin(WebKitWebView* page, GArray *argv) {
352     (void) page; (void) argv;
353     gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
354 }
355
356 static void scroll_end(WebKitWebView* page, GArray *argv) {
357     (void) page; (void) argv;
358     gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
359                               gtk_adjustment_get_page_size(uzbl.gui.bar_v));
360 }
361
362 static void scroll_vert(WebKitWebView* page, GArray *argv) {
363     (void) page;
364     scroll(uzbl.gui.bar_v, argv);
365 }
366
367 static void scroll_horz(WebKitWebView* page, GArray *argv) {
368     (void) page;
369     scroll(uzbl.gui.bar_h, argv);
370 }
371
372 static void
373 cmd_set_status() {
374     if (!uzbl.behave.show_status) {
375         gtk_widget_hide(uzbl.gui.mainbar);
376     } else {
377         gtk_widget_show(uzbl.gui.mainbar);
378     }
379     update_title();
380 }
381
382 static void
383 toggle_status_cb (WebKitWebView* page, GArray *argv) {
384     (void)page;
385     (void)argv;
386
387     if (uzbl.behave.show_status) {
388         gtk_widget_hide(uzbl.gui.mainbar);
389     } else {
390         gtk_widget_show(uzbl.gui.mainbar);
391     }
392     uzbl.behave.show_status = !uzbl.behave.show_status;
393     update_title();
394 }
395
396 static void
397 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
398     (void) page;
399     (void) title;
400     (void) data;
401     //Set selected_url state variable
402     g_free(uzbl.state.selected_url);
403     uzbl.state.selected_url = NULL;
404     if (link) {
405         uzbl.state.selected_url = g_strdup(link);
406     }
407     update_title();
408 }
409
410 static void
411 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
412     (void) web_view;
413     (void) web_frame;
414     (void) data;
415     if (uzbl.gui.main_title)
416         g_free (uzbl.gui.main_title);
417     uzbl.gui.main_title = g_strdup (title);
418     update_title();
419 }
420
421 static void
422 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
423     (void) page;
424     (void) data;
425     uzbl.gui.sbar.load_progress = progress;
426     update_title();
427 }
428
429 static void
430 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
431     (void) page;
432     (void) frame;
433     (void) data;
434     if (uzbl.behave.load_finish_handler)
435         run_handler(uzbl.behave.load_finish_handler, "");
436 }
437
438 static void
439 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
440     (void) page;
441     (void) frame;
442     (void) data;
443     g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
444     if (uzbl.behave.load_start_handler)
445         run_handler(uzbl.behave.load_start_handler, "");
446 }
447
448 static void
449 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
450     (void) page;
451     (void) data;
452     g_free (uzbl.state.uri);
453     GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
454     uzbl.state.uri = g_string_free (newuri, FALSE);
455     if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
456         uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
457         update_title();
458     }
459     if (uzbl.behave.load_commit_handler)
460         run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
461 }
462
463 static void
464 destroy_cb (GtkWidget* widget, gpointer data) {
465     (void) widget;
466     (void) data;
467     gtk_main_quit ();
468 }
469
470 static void
471 log_history_cb () {
472    if (uzbl.behave.history_handler) {
473        time_t rawtime;
474        struct tm * timeinfo;
475        char date [80];
476        time ( &rawtime );
477        timeinfo = localtime ( &rawtime );
478        strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
479        run_handler(uzbl.behave.history_handler, date);
480    }
481 }
482
483
484 /* VIEW funcs (little webkit wrappers) */
485 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv){(void)argv; webkit_web_view_##name(page);}
486 VIEWFUNC(reload)
487 VIEWFUNC(reload_bypass_cache)
488 VIEWFUNC(stop_loading)
489 VIEWFUNC(zoom_in)
490 VIEWFUNC(zoom_out)
491 VIEWFUNC(go_back)
492 VIEWFUNC(go_forward)
493 #undef VIEWFUNC
494
495 /* -- command to callback/function map for things we cannot attach to any signals */
496 // TODO: reload
497 static struct {char *name; Command command[2];} cmdlist[] =
498 {   /* key                   function      no_split      */
499     { "back",               {view_go_back, 0}              },
500     { "forward",            {view_go_forward, 0}           },
501     { "scroll_vert",        {scroll_vert, 0}               },
502     { "scroll_horz",        {scroll_horz, 0}               },
503     { "scroll_begin",       {scroll_begin, 0}              },
504     { "scroll_end",         {scroll_end, 0}                },
505     { "reload",             {view_reload, 0},              },
506     { "reload_ign_cache",   {view_reload_bypass_cache, 0}  },
507     { "stop",               {view_stop_loading, 0},        },
508     { "zoom_in",            {view_zoom_in, 0},             }, //Can crash (when max zoom reached?).
509     { "zoom_out",           {view_zoom_out, 0},            },
510     { "uri",                {load_uri, NOSPLIT}            },
511     { "js",                 {run_js, NOSPLIT}              },
512     { "script",             {run_external_js, 0}           },
513     { "toggle_status",      {toggle_status_cb, 0}          },
514     { "spawn",              {spawn, 0}                     },
515     { "sync_spawn",         {spawn_sync, 0}                }, // needed for cookie handler
516     { "sh",                 {spawn_sh, 0}                  },
517     { "sync_sh",            {spawn_sh_sync, 0}             }, // needed for cookie handler
518     { "exit",               {close_uzbl, 0}                },
519     { "search",             {search_forward_text, NOSPLIT} },
520     { "search_reverse",     {search_reverse_text, NOSPLIT} },
521     { "toggle_insert_mode", {toggle_insert_mode, 0}        },
522     { "runcmd",             {runcmd, NOSPLIT}              }
523 };
524
525 static void
526 commands_hash(void)
527 {
528     unsigned int i;
529     uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
530
531     for (i = 0; i < LENGTH(cmdlist); i++)
532         g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
533 }
534
535 /* -- CORE FUNCTIONS -- */
536
537 void
538 free_action(gpointer act) {
539     Action *action = (Action*)act;
540     g_free(action->name);
541     if (action->param)
542         g_free(action->param);
543     g_free(action);
544 }
545
546 Action*
547 new_action(const gchar *name, const gchar *param) {
548     Action *action = g_new(Action, 1);
549
550     action->name = g_strdup(name);
551     if (param)
552         action->param = g_strdup(param);
553     else
554         action->param = NULL;
555
556     return action;
557 }
558
559 static bool
560 file_exists (const char * filename) {
561     return (access(filename, F_OK) == 0);
562 }
563
564 static void
565 toggle_insert_mode(WebKitWebView *page, GArray *argv) {
566     (void)page;
567
568     if (argv_idx(argv, 0)) {
569         if (strcmp (argv_idx(argv, 0), "0") == 0) {
570             uzbl.behave.insert_mode = FALSE;
571         } else {
572             uzbl.behave.insert_mode = TRUE;
573         }
574     } else {
575         uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
576     }
577
578     update_title();
579 }
580
581 static void
582 load_uri (WebKitWebView *web_view, GArray *argv) {
583     if (argv_idx(argv, 0)) {
584         GString* newuri = g_string_new (argv_idx(argv, 0));
585         if (g_strrstr (argv_idx(argv, 0), "://") == NULL)
586             g_string_prepend (newuri, "http://");
587         /* if we do handle cookies, ask our handler for them */
588         webkit_web_view_load_uri (web_view, newuri->str);
589         g_string_free (newuri, TRUE);
590     }
591 }
592
593 static void
594 run_js (WebKitWebView * web_view, GArray *argv) {
595     if (argv_idx(argv, 0))
596         webkit_web_view_execute_script (web_view, argv_idx(argv, 0));
597 }
598
599 static void
600 run_external_js (WebKitWebView * web_view, GArray *argv) {
601     if (argv_idx(argv, 0)) {
602         GArray* lines = read_file_by_line (argv_idx (argv, 0));
603         gchar*  js = NULL;
604         int i = 0;
605         gchar* line;
606
607         while ((line = g_array_index(lines, gchar*, i))) {
608             if (js == NULL) {
609                 js = g_strdup (line);
610             } else {
611                 gchar* newjs = g_strconcat (js, line, NULL);
612                 js = newjs;
613             }
614             i ++;
615             g_free (line);
616         }
617         
618         if (uzbl.state.verbose)
619             printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
620
621         if (argv_idx (argv, 1)) {
622             gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
623             g_free (js);
624             js = newjs;
625         }
626         webkit_web_view_execute_script (web_view, js);
627         g_free (js);
628         g_array_free (lines, TRUE);
629     }
630 }
631
632 static void
633 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
634     if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
635         if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
636             webkit_web_view_unmark_text_matches (page);
637             webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
638             uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
639         }
640     }
641     
642     if (uzbl.state.searchtx) {
643         if (uzbl.state.verbose)
644             printf ("Searching: %s\n", uzbl.state.searchtx);
645         webkit_web_view_set_highlight_text_matches (page, TRUE);
646         webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
647     }
648 }
649
650 static void
651 search_forward_text (WebKitWebView *page, GArray *argv) {
652     search_text(page, argv, TRUE);
653 }
654
655 static void
656 search_reverse_text (WebKitWebView *page, GArray *argv) {
657     search_text(page, argv, FALSE);
658 }
659
660 static void
661 new_window_load_uri (const gchar * uri) {
662     GString* to_execute = g_string_new ("");
663     g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
664     int i;
665     for (i = 0; entries[i].long_name != NULL; i++) {
666         if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
667             gchar** str = (gchar**)entries[i].arg_data;
668             if (*str!=NULL) {
669                 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
670             }
671         }
672     }
673     if (uzbl.state.verbose)
674         printf("\n%s\n", to_execute->str);
675     g_spawn_command_line_async (to_execute->str, NULL);
676     g_string_free (to_execute, TRUE);
677 }
678
679 static void
680 close_uzbl (WebKitWebView *page, GArray *argv) {
681     (void)page;
682     (void)argv;
683     gtk_main_quit ();
684 }
685
686 /* --Statusbar functions-- */
687 static char*
688 build_progressbar_ascii(int percent) {
689    int width=uzbl.gui.sbar.progress_w;
690    int i;
691    double l;
692    GString *bar = g_string_new("");
693
694    l = (double)percent*((double)width/100.);
695    l = (int)(l+.5)>=(int)l ? l+.5 : l;
696
697    for(i=0; i<(int)l; i++)
698        g_string_append(bar, uzbl.gui.sbar.progress_s);
699
700    for(; i<width; i++)
701        g_string_append(bar, uzbl.gui.sbar.progress_u);
702
703    return g_string_free(bar, FALSE);
704 }
705
706 static void
707 setup_scanner() {
708      const GScannerConfig scan_config = {
709              (
710               "\t\r\n"
711              )            /* cset_skip_characters */,
712              (
713               G_CSET_a_2_z
714               "_#"
715               G_CSET_A_2_Z
716              )            /* cset_identifier_first */,
717              (
718               G_CSET_a_2_z
719               "_0123456789"
720               G_CSET_A_2_Z
721               G_CSET_LATINS
722               G_CSET_LATINC
723              )            /* cset_identifier_nth */,
724              ( "" )    /* cpair_comment_single */,
725
726              TRUE         /* case_sensitive */,
727
728              FALSE        /* skip_comment_multi */,
729              FALSE        /* skip_comment_single */,
730              FALSE        /* scan_comment_multi */,
731              TRUE         /* scan_identifier */,
732              TRUE         /* scan_identifier_1char */,
733              FALSE        /* scan_identifier_NULL */,
734              TRUE         /* scan_symbols */,
735              FALSE        /* scan_binary */,
736              FALSE        /* scan_octal */,
737              FALSE        /* scan_float */,
738              FALSE        /* scan_hex */,
739              FALSE        /* scan_hex_dollar */,
740              FALSE        /* scan_string_sq */,
741              FALSE        /* scan_string_dq */,
742              TRUE         /* numbers_2_int */,
743              FALSE        /* int_2_float */,
744              FALSE        /* identifier_2_string */,
745              FALSE        /* char_2_token */,
746              FALSE        /* symbol_2_token */,
747              TRUE         /* scope_0_fallback */,
748              FALSE,
749              TRUE
750      };
751
752      uzbl.scan = g_scanner_new(&scan_config);
753      while(symp->symbol_name) {
754          g_scanner_scope_add_symbol(uzbl.scan, 0,
755                          symp->symbol_name,
756                          GINT_TO_POINTER(symp->symbol_token));
757          symp++;
758      }
759 }
760
761 static gchar *
762 expand_template(const char *template, gboolean escape_markup) {
763      if(!template) return NULL;
764
765      GTokenType token = G_TOKEN_NONE;
766      GString *ret = g_string_new("");
767      char *buf=NULL;
768      int sym;
769
770      g_scanner_input_text(uzbl.scan, template, strlen(template));
771      while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
772          token = g_scanner_get_next_token(uzbl.scan);
773
774          if(token == G_TOKEN_SYMBOL) {
775              sym = (int)g_scanner_cur_value(uzbl.scan).v_symbol;
776              switch(sym) {
777                  case SYM_URI:
778                      if(escape_markup) {
779                          buf = uzbl.state.uri?
780                              g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
781                          g_string_append(ret, buf);
782                          g_free(buf);
783                      }
784                      else
785                          g_string_append(ret, uzbl.state.uri?
786                                  uzbl.state.uri:g_strdup(""));
787                      break;
788                  case SYM_LOADPRGS:
789                      buf = itos(uzbl.gui.sbar.load_progress);
790                      g_string_append(ret, buf);
791                      g_free(buf);
792                      break;
793                  case SYM_LOADPRGSBAR:
794                      buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
795                      g_string_append(ret, buf);
796                      g_free(buf);
797                      break;
798                  case SYM_TITLE:
799                      if(escape_markup) {
800                          buf = uzbl.gui.main_title?
801                              g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
802                          g_string_append(ret, buf);
803                          g_free(buf);
804                      }
805                      else
806                          g_string_append(ret, uzbl.gui.main_title?
807                                  uzbl.gui.main_title:g_strdup(""));
808                      break;
809                  case SYM_SELECTED_URI:
810                      if(escape_markup) {
811                          buf = uzbl.state.selected_url?
812                              g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
813                          g_string_append(ret, buf);
814                          g_free(buf);
815                      }
816                      else
817                          g_string_append(ret, uzbl.state.selected_url?
818                                  uzbl.state.selected_url:g_strdup(""));
819                      break;
820                  case SYM_NAME:
821                      buf = itos(uzbl.xwin);
822                      g_string_append(ret,
823                              uzbl.state.instance_name?uzbl.state.instance_name:buf);
824                      g_free(buf);
825                      break;
826                  case SYM_KEYCMD:
827                      if(escape_markup) {
828                          buf = uzbl.state.keycmd->str?
829                              g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
830                          g_string_append(ret, buf);
831                          g_free(buf);
832                      }
833                      else
834                          g_string_append(ret, uzbl.state.keycmd->str?
835                                  uzbl.state.keycmd->str:g_strdup(""));
836                      break;
837                  case SYM_MODE:
838                      g_string_append(ret,
839                              uzbl.behave.insert_mode?"[I]":"[C]");
840                      break;
841                  case SYM_MSG:
842                      g_string_append(ret,
843                              uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
844                      break;
845                      /* useragent syms */
846                  case SYM_WK_MAJ:
847                      buf = itos(WEBKIT_MAJOR_VERSION);
848                      g_string_append(ret, buf);
849                      g_free(buf);
850                      break;
851                  case SYM_WK_MIN:
852                      buf = itos(WEBKIT_MINOR_VERSION);
853                      g_string_append(ret, buf);
854                      g_free(buf);
855                      break;
856                  case SYM_WK_MIC:
857                      buf = itos(WEBKIT_MICRO_VERSION);
858                      g_string_append(ret, buf);
859                      g_free(buf);
860                      break;
861                  case SYM_SYSNAME:
862                      g_string_append(ret, uzbl.state.unameinfo.sysname);
863                      break;
864                  case SYM_NODENAME:
865                      g_string_append(ret, uzbl.state.unameinfo.nodename);
866                      break;
867                  case SYM_KERNREL:
868                      g_string_append(ret, uzbl.state.unameinfo.release);
869                      break;
870                  case SYM_KERNVER:
871                      g_string_append(ret, uzbl.state.unameinfo.version);
872                      break;
873                  case SYM_ARCHSYS:
874                      g_string_append(ret, uzbl.state.unameinfo.machine);
875                      break;
876                  case SYM_ARCHUZBL:
877                      g_string_append(ret, ARCH);
878                      break;
879 #ifdef _GNU_SOURCE
880                  case SYM_DOMAINNAME:
881                      g_string_append(ret, uzbl.state.unameinfo.domainname);
882                      break;
883 #endif
884                  case SYM_COMMIT:
885                      g_string_append(ret, COMMIT);
886                      break;
887                  default:
888                      break;
889              }
890          }
891          else if(token == G_TOKEN_INT) {
892              buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
893              g_string_append(ret, buf);
894              g_free(buf);
895          }
896          else if(token == G_TOKEN_IDENTIFIER) {
897              g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
898          }
899          else if(token == G_TOKEN_CHAR) {
900              g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
901          }
902      }
903
904      return g_string_free(ret, FALSE);
905 }
906 /* --End Statusbar functions-- */
907
908 static void
909 sharg_append(GArray *a, const gchar *str) {
910     const gchar *s = (str ? str : "");
911     g_array_append_val(a, s);
912 }
913
914 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
915 static gboolean
916 run_command (const gchar *command, const guint npre, const gchar **args,
917              const gboolean sync, char **stdout) {
918    //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
919     GError *err = NULL;
920     
921     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
922     gchar *pid = itos(getpid());
923     gchar *xwin = itos(uzbl.xwin);
924     guint i;
925     sharg_append(a, command);
926     for (i = 0; i < npre; i++) /* add n args before the default vars */
927         sharg_append(a, args[i]);
928     sharg_append(a, uzbl.state.config_file);
929     sharg_append(a, pid);
930     sharg_append(a, xwin);
931     sharg_append(a, uzbl.comm.fifo_path);
932     sharg_append(a, uzbl.comm.socket_path);
933     sharg_append(a, uzbl.state.uri);
934     sharg_append(a, uzbl.gui.main_title);
935
936     for (i = npre; i < g_strv_length((gchar**)args); i++)
937         sharg_append(a, args[i]);
938     
939     gboolean result;
940     if (sync) {
941         if (*stdout) *stdout = strfree(*stdout);
942         
943         result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
944                               NULL, NULL, stdout, NULL, NULL, &err);
945     } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
946                                   NULL, NULL, NULL, &err);
947
948     if (uzbl.state.verbose) {
949         GString *s = g_string_new("spawned:");
950         for (i = 0; i < (a->len); i++) {
951             gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
952             g_string_append_printf(s, " %s", qarg);
953             g_free (qarg);
954         }
955         g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
956         printf("%s\n", s->str);
957         g_string_free(s, TRUE);
958     }
959     if (err) {
960         g_printerr("error on run_command: %s\n", err->message);
961         g_error_free (err);
962     }
963     g_free (pid);
964     g_free (xwin);
965     g_array_free (a, TRUE);
966     return result;
967 }
968
969 static gchar**
970 split_quoted(const gchar* src, const gboolean unquote) {
971     /* split on unquoted space, return array of strings;
972        remove a layer of quotes and backslashes if unquote */
973     if (!src) return NULL;
974     
975     gboolean dq = FALSE;
976     gboolean sq = FALSE;
977     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
978     GString *s = g_string_new ("");
979     const gchar *p;
980     gchar **ret;
981     gchar *dup;
982     for (p = src; *p != '\0'; p++) {
983         if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
984         else if (*p == '\\') { g_string_append_c(s, *p++);
985                                g_string_append_c(s, *p); }
986         else if ((*p == '"') && unquote && !sq) dq = !dq;
987         else if (*p == '"' && !sq) { g_string_append_c(s, *p);
988                                      dq = !dq; }
989         else if ((*p == '\'') && unquote && !dq) sq = !sq;
990         else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
991                                       sq = ! sq; }
992         else if ((*p == ' ') && !dq && !sq) {
993             dup = g_strdup(s->str);
994             g_array_append_val(a, dup);
995             g_string_truncate(s, 0);
996         } else g_string_append_c(s, *p);
997     }
998     dup = g_strdup(s->str);
999     g_array_append_val(a, dup);
1000     ret = (gchar**)a->data;
1001     g_array_free (a, FALSE);
1002     g_string_free (s, TRUE);
1003     return ret;
1004 }
1005
1006 static void
1007 spawn(WebKitWebView *web_view, GArray *argv) {
1008     (void)web_view;
1009     //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1010     if (argv_idx(argv, 0))
1011         run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1012 }
1013
1014 static void
1015 spawn_sync(WebKitWebView *web_view, GArray *argv) {
1016     (void)web_view;
1017     
1018     if (argv_idx(argv, 0))
1019         run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1020                     TRUE, &uzbl.comm.sync_stdout);
1021 }
1022
1023 static void
1024 spawn_sh(WebKitWebView *web_view, GArray *argv) {
1025     (void)web_view;
1026     if (!uzbl.behave.shell_cmd) {
1027         g_printerr ("spawn_sh: shell_cmd is not set!\n");
1028         return;
1029     }
1030     
1031     guint i;
1032     gchar *spacer = g_strdup("");
1033     g_array_insert_val(argv, 1, spacer);
1034     gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1035
1036     for (i = 1; i < g_strv_length(cmd); i++)
1037         g_array_prepend_val(argv, cmd[i]);
1038
1039     if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1040     g_free (spacer);
1041     g_strfreev (cmd);
1042 }
1043
1044 static void
1045 spawn_sh_sync(WebKitWebView *web_view, GArray *argv) {
1046     (void)web_view;
1047     if (!uzbl.behave.shell_cmd) {
1048         g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1049         return;
1050     }
1051     
1052     guint i;
1053     gchar *spacer = g_strdup("");
1054     g_array_insert_val(argv, 1, spacer);
1055     gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1056
1057     for (i = 1; i < g_strv_length(cmd); i++)
1058         g_array_prepend_val(argv, cmd[i]);
1059
1060     if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1061                          TRUE, &uzbl.comm.sync_stdout);
1062     g_free (spacer);
1063     g_strfreev (cmd);
1064 }
1065
1066 static void
1067 parse_command(const char *cmd, const char *param) {
1068     Command *c;
1069
1070     if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1071
1072             guint i;
1073             gchar **par = split_quoted(param, TRUE);
1074             GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1075
1076             if (c[1] == NOSPLIT) { /* don't split */
1077                 sharg_append(a, param);
1078             } else if (par) {
1079                 for (i = 0; i < g_strv_length(par); i++)
1080                     sharg_append(a, par[i]);
1081             }
1082             c[0](uzbl.gui.web_view, a);
1083             g_strfreev (par);
1084             g_array_free (a, TRUE);
1085
1086     } else
1087         g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1088 }
1089
1090 /* command parser */
1091 static void
1092 setup_regex() {
1093     uzbl.comm.get_regex  = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
1094             G_REGEX_OPTIMIZE, 0, NULL);
1095     uzbl.comm.set_regex  = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
1096             G_REGEX_OPTIMIZE, 0, NULL);
1097     uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
1098             G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, NULL);
1099     uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
1100             G_REGEX_OPTIMIZE, 0, NULL);
1101     uzbl.comm.keycmd_regex = g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
1102             G_REGEX_OPTIMIZE, 0, NULL);
1103 }
1104
1105 static gboolean
1106 get_var_value(gchar *name) {
1107     uzbl_cmdprop *c;
1108
1109     if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1110         if(c->type == TYPE_STR)
1111             printf("VAR: %s VALUE: %s\n", name, (char *)*c->ptr);
1112         else if(c->type == TYPE_INT)
1113             printf("VAR: %s VALUE: %d\n", name, (int)*c->ptr);
1114     }
1115     return TRUE;
1116 }
1117
1118 static void
1119 set_proxy_url() {
1120     SoupURI *suri;
1121
1122     if(*uzbl.net.proxy_url == ' '
1123        || uzbl.net.proxy_url == NULL) {
1124         soup_session_remove_feature_by_type(uzbl.net.soup_session,
1125                 (GType) SOUP_SESSION_PROXY_URI);
1126     }
1127     else {
1128         suri = soup_uri_new(uzbl.net.proxy_url);
1129         g_object_set(G_OBJECT(uzbl.net.soup_session),
1130                 SOUP_SESSION_PROXY_URI,
1131                 suri, NULL);
1132         soup_uri_free(suri);
1133     }
1134     return;
1135 }
1136
1137 static void
1138 cmd_load_uri() {
1139     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1140     g_array_append_val (a, uzbl.state.uri);
1141     load_uri(uzbl.gui.web_view, a);
1142     g_array_free (a, TRUE);
1143 }
1144
1145 static void 
1146 cmd_always_insert_mode() {
1147     uzbl.behave.insert_mode =
1148         uzbl.behave.always_insert_mode ?  TRUE : FALSE;
1149     update_title();
1150 }
1151
1152 static void
1153 cmd_max_conns() {
1154     g_object_set(G_OBJECT(uzbl.net.soup_session),
1155             SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1156 }
1157
1158 static void
1159 cmd_max_conns_host() {
1160     g_object_set(G_OBJECT(uzbl.net.soup_session),
1161             SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1162 }
1163
1164 static void
1165 cmd_http_debug() {
1166     soup_session_remove_feature
1167         (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1168     /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1169     /*g_free(uzbl.net.soup_logger);*/
1170
1171     uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1172     soup_session_add_feature(uzbl.net.soup_session,
1173             SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1174 }
1175
1176 static WebKitWebSettings*
1177 view_settings() {
1178     return webkit_web_view_get_settings(uzbl.gui.web_view);
1179 }
1180
1181 static void
1182 cmd_font_size() {
1183     WebKitWebSettings *ws = view_settings();
1184     if (uzbl.behave.font_size > 0) {
1185         g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1186     }
1187     
1188     if (uzbl.behave.monospace_size > 0) {
1189         g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1190                       uzbl.behave.monospace_size, NULL);
1191     } else {
1192         g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1193                       uzbl.behave.font_size, NULL);
1194     }
1195 }
1196
1197 static void
1198 cmd_disable_plugins() {
1199     g_object_set (G_OBJECT(view_settings()), "enable-plugins", 
1200             !uzbl.behave.disable_plugins, NULL);
1201 }
1202
1203 static void
1204 cmd_disable_scripts() {
1205     g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1206             !uzbl.behave.disable_plugins, NULL);
1207 }
1208
1209 static void
1210 cmd_minimum_font_size() {
1211     g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1212             uzbl.behave.minimum_font_size, NULL);
1213 }
1214 static void
1215 cmd_autoload_img() {
1216     g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1217             uzbl.behave.autoload_img, NULL);
1218 }
1219
1220
1221 static void
1222 cmd_autoshrink_img() {
1223     g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1224             uzbl.behave.autoshrink_img, NULL);
1225 }
1226
1227
1228 static void
1229 cmd_enable_spellcheck() {
1230     g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1231             uzbl.behave.enable_spellcheck, NULL);
1232 }
1233
1234 static void
1235 cmd_enable_private() {
1236     g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1237             uzbl.behave.enable_private, NULL);
1238 }
1239
1240 static void
1241 cmd_print_bg() {
1242     g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1243             uzbl.behave.print_bg, NULL);
1244 }
1245
1246 static void 
1247 cmd_style_uri() {
1248     g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1249             uzbl.behave.style_uri, NULL);
1250 }
1251
1252 static void
1253 cmd_cookie_handler() {
1254     gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1255     if ((g_strcmp0(split[0], "sh") == 0) ||
1256         (g_strcmp0(split[0], "spawn") == 0)) {
1257         g_free (uzbl.behave.cookie_handler);
1258         uzbl.behave.cookie_handler =
1259             g_strdup_printf("sync_%s %s", split[0], split[1]);
1260     }
1261     g_strfreev (split);
1262 }
1263
1264 static void
1265 cmd_fifo_dir() {
1266     uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1267 }
1268
1269 static void
1270 cmd_socket_dir() {
1271     uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1272 }
1273
1274 static void
1275 cmd_modkey() {
1276     int i;
1277     char *buf;
1278
1279     buf = g_utf8_strup(uzbl.behave.modkey, -1);
1280     uzbl.behave.modmask = 0;
1281
1282     if(uzbl.behave.modkey) 
1283         g_free(uzbl.behave.modkey);
1284     uzbl.behave.modkey = buf;
1285
1286     for (i = 0; modkeys[i].key != NULL; i++) {
1287         if (g_strrstr(buf, modkeys[i].key))
1288             uzbl.behave.modmask |= modkeys[i].mask;
1289     }
1290 }
1291
1292 static void
1293 cmd_useragent() {
1294     if (*uzbl.net.useragent == ' ') {
1295         g_free (uzbl.net.useragent);
1296         uzbl.net.useragent = NULL;
1297     } else {
1298         gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1299         if (ua)
1300             g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1301         g_free(uzbl.net.useragent);
1302         uzbl.net.useragent = ua;
1303     }
1304 }
1305
1306 static void
1307 move_statusbar() {
1308     gtk_widget_ref(uzbl.gui.scrolled_win);
1309     gtk_widget_ref(uzbl.gui.mainbar);
1310     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1311     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1312
1313     if(uzbl.behave.status_top) {
1314         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1315         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1316     }
1317     else {
1318         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1319         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1320     }
1321     gtk_widget_unref(uzbl.gui.scrolled_win);
1322     gtk_widget_unref(uzbl.gui.mainbar);
1323     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1324     return;
1325 }
1326
1327 static gboolean
1328 set_var_value(gchar *name, gchar *val) {
1329     uzbl_cmdprop *c = NULL;
1330     char *endp = NULL;
1331
1332     if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1333         /* check for the variable type */
1334         if (c->type == TYPE_STR) {
1335             g_free(*c->ptr);
1336             *c->ptr = g_strdup(val);
1337         } else if(c->type == TYPE_INT) {
1338             int *ip = GPOINTER_TO_INT(c->ptr);
1339             *ip = (int)strtoul(val, &endp, 10);
1340         }
1341
1342         /* invoke a command specific function */
1343         if(c->func) c->func();
1344     }
1345     return TRUE;
1346 }
1347
1348 static void
1349 runcmd(WebKitWebView* page, GArray *argv) {
1350     (void) page;
1351     parse_cmd_line(argv_idx(argv, 0));
1352 }
1353
1354 static void
1355 parse_cmd_line(const char *ctl_line) {
1356     gchar **tokens = NULL;
1357
1358     /* SET command */
1359     if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1360         tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1361         if(tokens[0][0] == 0) {
1362             gchar* value = parseenv(g_strdup(tokens[2]));
1363             set_var_value(tokens[1], value);
1364             g_free(value);
1365         }
1366         else
1367             printf("Error in command: %s\n", tokens[0]);
1368     }
1369     /* GET command */
1370     else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1371         tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1372         if(tokens[0][0] == 0) {
1373             get_var_value(tokens[1]);
1374         }
1375         else
1376             printf("Error in command: %s\n", tokens[0]);
1377     }
1378     /* BIND command */
1379     else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1380         tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1381         if(tokens[0][0] == 0) {
1382             gchar* value = parseenv(g_strdup(tokens[2]));
1383             add_binding(tokens[1], value);
1384             g_free(value);
1385         }
1386         else
1387             printf("Error in command: %s\n", tokens[0]);
1388     }
1389     /* ACT command */
1390     else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1391         tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1392         if(tokens[0][0] == 0) {
1393             parse_command(tokens[1], tokens[2]);
1394         }
1395         else
1396             printf("Error in command: %s\n", tokens[0]);
1397     }
1398     /* KEYCMD command */
1399     else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1400         tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1401         if(tokens[0][0] == 0) {
1402             /* should incremental commands want each individual "keystroke"
1403                sent in a loop or the whole string in one go like now? */
1404             g_string_assign(uzbl.state.keycmd, tokens[1]);
1405             run_keycmd(FALSE);
1406             if (g_strstr_len(ctl_line, 7, "n") || g_strstr_len(ctl_line, 7, "N"))
1407                 run_keycmd(TRUE);
1408             update_title();
1409         }
1410     }
1411     /* Comments */
1412     else if(   (ctl_line[0] == '#')
1413             || (ctl_line[0] == ' ')
1414             || (ctl_line[0] == '\n'))
1415         ; /* ignore these lines */
1416     else
1417         printf("Command not understood (%s)\n", ctl_line);
1418
1419     if(tokens)
1420         g_strfreev(tokens);
1421
1422     return;
1423 }
1424
1425 static gchar*
1426 build_stream_name(int type, const gchar* dir) {
1427     char *xwin_str;
1428     State *s = &uzbl.state;
1429     gchar *str;
1430
1431     xwin_str = itos((int)uzbl.xwin);
1432     if (type == FIFO) {
1433         str = g_strdup_printf
1434             ("%s/uzbl_fifo_%s", dir,
1435              s->instance_name ? s->instance_name : xwin_str);
1436     } else if (type == SOCKET) {
1437         str = g_strdup_printf
1438             ("%s/uzbl_socket_%s", dir,
1439              s->instance_name ? s->instance_name : xwin_str );
1440     }
1441     g_free(xwin_str);
1442     return str;
1443 }
1444
1445 static gboolean
1446 control_fifo(GIOChannel *gio, GIOCondition condition) {
1447     if (uzbl.state.verbose)
1448         printf("triggered\n");
1449     gchar *ctl_line;
1450     GIOStatus ret;
1451     GError *err = NULL;
1452
1453     if (condition & G_IO_HUP)
1454         g_error ("Fifo: Read end of pipe died!\n");
1455
1456     if(!gio)
1457        g_error ("Fifo: GIOChannel broke\n");
1458
1459     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1460     if (ret == G_IO_STATUS_ERROR) {
1461         g_error ("Fifo: Error reading: %s\n", err->message);
1462         g_error_free (err);
1463     }
1464
1465     parse_cmd_line(ctl_line);
1466     g_free(ctl_line);
1467
1468     return TRUE;
1469 }
1470
1471 static gchar*
1472 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1473     if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1474         if (unlink(uzbl.comm.fifo_path) == -1)
1475             g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1476         g_free(uzbl.comm.fifo_path);
1477         uzbl.comm.fifo_path = NULL;
1478     }
1479
1480     if (*dir == ' ') { /* space unsets the variable */
1481         g_free (dir);
1482         return NULL;
1483     }
1484
1485     GIOChannel *chan = NULL;
1486     GError *error = NULL;
1487     gchar *path = build_stream_name(FIFO, dir);
1488
1489     if (!file_exists(path)) {
1490         if (mkfifo (path, 0666) == 0) {
1491             // 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.
1492             chan = g_io_channel_new_file(path, "r+", &error);
1493             if (chan) {
1494                 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1495                     if (uzbl.state.verbose)
1496                         printf ("init_fifo: created successfully as %s\n", path);
1497                     uzbl.comm.fifo_path = path;
1498                     return dir;
1499                 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1500             } else g_warning ("init_fifo: can't open: %s\n", error->message);
1501         } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1502     } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1503
1504     /* if we got this far, there was an error; cleanup */
1505     if (error) g_error_free (error);
1506     g_free(dir);
1507     g_free(path);
1508     return NULL;
1509 }
1510
1511 static gboolean
1512 control_stdin(GIOChannel *gio, GIOCondition condition) {
1513     (void) condition;
1514     gchar *ctl_line = NULL;
1515     GIOStatus ret;
1516
1517     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1518     if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1519         return FALSE;
1520
1521     parse_cmd_line(ctl_line);
1522     g_free(ctl_line);
1523
1524     return TRUE;
1525 }
1526
1527 static void
1528 create_stdin () {
1529     GIOChannel *chan = NULL;
1530     GError *error = NULL;
1531
1532     chan = g_io_channel_unix_new(fileno(stdin));
1533     if (chan) {
1534         if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1535             g_error ("Stdin: could not add watch\n");
1536         } else {
1537             if (uzbl.state.verbose)
1538                 printf ("Stdin: watch added successfully\n");
1539         }
1540     } else {
1541         g_error ("Stdin: Error while opening: %s\n", error->message);
1542     }
1543     if (error) g_error_free (error);
1544 }
1545
1546 static gboolean
1547 control_socket(GIOChannel *chan) {
1548     struct sockaddr_un remote;
1549     char buffer[512], *ctl_line;
1550     char temp[128];
1551     int sock, clientsock, n, done;
1552     unsigned int t;
1553
1554     sock = g_io_channel_unix_get_fd(chan);
1555
1556     memset (buffer, 0, sizeof (buffer));
1557
1558     t          = sizeof (remote);
1559     clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1560
1561     done = 0;
1562     do {
1563         memset (temp, 0, sizeof (temp));
1564         n = recv (clientsock, temp, 128, 0);
1565         if (n == 0) {
1566             buffer[strlen (buffer)] = '\0';
1567             done = 1;
1568         }
1569         if (!done)
1570             strcat (buffer, temp);
1571     } while (!done);
1572
1573     if (strcmp (buffer, "\n") < 0) {
1574         buffer[strlen (buffer) - 1] = '\0';
1575     } else {
1576         buffer[strlen (buffer)] = '\0';
1577     }
1578     close (clientsock);
1579     ctl_line = g_strdup(buffer);
1580     parse_cmd_line (ctl_line);
1581
1582 /*
1583    TODO: we should be able to do it with this.  but glib errors out with "Invalid argument"
1584     GError *error = NULL;
1585     gsize len;
1586     GIOStatus ret;
1587     ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1588     if (ret == G_IO_STATUS_ERROR)
1589         g_error ("Error reading: %s\n", error->message);
1590
1591     printf("Got line %s (%u bytes) \n",ctl_line, len);
1592     if(ctl_line) {
1593        parse_line(ctl_line);
1594 */
1595
1596     g_free(ctl_line);
1597     return TRUE;
1598 }
1599
1600 static gchar*
1601 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1602     if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1603         if (unlink(uzbl.comm.socket_path) == -1)
1604             g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1605         g_free(uzbl.comm.socket_path);
1606         uzbl.comm.socket_path = NULL;
1607     }
1608
1609     if (*dir == ' ') {
1610         g_free(dir);
1611         return NULL;
1612     }
1613
1614     GIOChannel *chan = NULL;
1615     int sock, len;
1616     struct sockaddr_un local;
1617     gchar *path = build_stream_name(SOCKET, dir);
1618
1619     sock = socket (AF_UNIX, SOCK_STREAM, 0);
1620
1621     local.sun_family = AF_UNIX;
1622     strcpy (local.sun_path, path);
1623     unlink (local.sun_path);
1624
1625     len = strlen (local.sun_path) + sizeof (local.sun_family);
1626     if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1627         if (uzbl.state.verbose)
1628             printf ("init_socket: opened in %s\n", path);
1629         listen (sock, 5);
1630
1631         if( (chan = g_io_channel_unix_new(sock)) ) {
1632             g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1633             uzbl.comm.socket_path = path;
1634             return dir;
1635         }
1636     } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1637
1638     /* if we got this far, there was an error; cleanup */
1639     g_free(path);
1640     g_free(dir);
1641     return NULL;
1642 }
1643
1644 /*
1645  NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1646  it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1647 */
1648 // this function may be called very early when the templates are not set (yet), hence the checks
1649 static void
1650 update_title (void) {
1651     Behaviour *b = &uzbl.behave;
1652     gchar *parsed;
1653
1654     if (b->show_status) {
1655         if (b->title_format_short) {
1656             parsed = expand_template(b->title_format_short, FALSE);
1657             gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1658             g_free(parsed);
1659         }
1660         if (b->status_format) {
1661             parsed = expand_template(b->status_format, TRUE);
1662             gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1663             g_free(parsed);
1664         }
1665         if (b->status_background) {
1666             GdkColor color;
1667             gdk_color_parse (b->status_background, &color);
1668             //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)
1669             gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1670         }
1671     } else {
1672         if (b->title_format_long) {
1673             parsed = expand_template(b->title_format_long, FALSE);
1674             gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1675             g_free(parsed);
1676         }
1677     }
1678 }
1679
1680 static gboolean
1681 key_press_cb (GtkWidget* window, GdkEventKey* event)
1682 {
1683     //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1684
1685     (void) window;
1686
1687     if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1688         || 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)
1689         return FALSE;
1690
1691     /* turn off insert mode (if always_insert_mode is not used) */
1692     if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1693         uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1694         update_title();
1695         return TRUE;
1696     }
1697
1698     if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1699         return FALSE;
1700
1701     if (event->keyval == GDK_Escape) {
1702         g_string_truncate(uzbl.state.keycmd, 0);
1703         update_title();
1704         return TRUE;
1705     }
1706
1707     //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1708     if (event->keyval == GDK_Insert) {
1709         gchar * str;
1710         if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1711             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1712         } else {
1713             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1714         }
1715         if (str) {
1716             g_string_append (uzbl.state.keycmd, str);
1717             update_title ();
1718             g_free (str);
1719         }
1720         return TRUE;
1721     }
1722
1723     if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1724         g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1725         update_title();
1726     }
1727
1728     gboolean key_ret = FALSE;
1729     if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1730         key_ret = TRUE;
1731     if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1732
1733     run_keycmd(key_ret);
1734     update_title();
1735     if (key_ret) return (!uzbl.behave.insert_mode);
1736     return TRUE;
1737 }
1738
1739 static void
1740 run_keycmd(const gboolean key_ret) {
1741     /* run the keycmd immediately if it isn't incremental and doesn't take args */
1742     Action *action;
1743     if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1744         g_string_truncate(uzbl.state.keycmd, 0);
1745         parse_command(action->name, action->param);
1746         return;
1747     }
1748
1749     /* try if it's an incremental keycmd or one that takes args, and run it */
1750     GString* short_keys = g_string_new ("");
1751     GString* short_keys_inc = g_string_new ("");
1752     unsigned int i;
1753     for (i=0; i<(uzbl.state.keycmd->len); i++) {
1754         g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1755         g_string_assign(short_keys_inc, short_keys->str);
1756         g_string_append_c(short_keys, '_');
1757         g_string_append_c(short_keys_inc, '*');
1758
1759         gboolean exec_now = FALSE;
1760         if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1761             if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1762         } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1763             if (key_ret) { /* just quit the incremental command on return */
1764                 g_string_truncate(uzbl.state.keycmd, 0);
1765                 break;
1766             } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1767         }
1768
1769         if (exec_now) {
1770             GString* parampart = g_string_new (uzbl.state.keycmd->str);
1771             GString* actionname = g_string_new ("");
1772             GString* actionparam = g_string_new ("");
1773             g_string_erase (parampart, 0, i+1);
1774             if (action->name)
1775                 g_string_printf (actionname, action->name, parampart->str);
1776             if (action->param)
1777                 g_string_printf (actionparam, action->param, parampart->str);
1778             parse_command(actionname->str, actionparam->str);
1779             g_string_free (actionname, TRUE);
1780             g_string_free (actionparam, TRUE);
1781             g_string_free (parampart, TRUE);
1782             if (key_ret)
1783                 g_string_truncate(uzbl.state.keycmd, 0);
1784             break;
1785         }
1786
1787         g_string_truncate(short_keys, short_keys->len - 1);
1788     }
1789     g_string_free (short_keys, TRUE);
1790     g_string_free (short_keys_inc, TRUE);
1791 }
1792
1793 static GtkWidget*
1794 create_browser () {
1795     GUI *g = &uzbl.gui;
1796
1797     GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1798     //main_window_ref = g_object_ref(scrolled_window);
1799     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
1800
1801     g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1802     gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1803
1804     g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1805     g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1806     g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1807     g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
1808     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1809     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1810     g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1811     g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1812     g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1813     g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1814
1815     return scrolled_window;
1816 }
1817
1818 static GtkWidget*
1819 create_mainbar () {
1820     GUI *g = &uzbl.gui;
1821
1822     g->mainbar = gtk_hbox_new (FALSE, 0);
1823
1824     /* keep a reference to the bar so we can re-pack it at runtime*/
1825     //sbar_ref = g_object_ref(g->mainbar);
1826
1827     g->mainbar_label = gtk_label_new ("");
1828     gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1829     gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1830     gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1831     gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1832     gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1833     return g->mainbar;
1834 }
1835
1836 static
1837 GtkWidget* create_window () {
1838     GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1839     gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1840     gtk_widget_set_name (window, "Uzbl browser");
1841     g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1842     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
1843
1844     return window;
1845 }
1846
1847 static void
1848 run_handler (const gchar *act, const gchar *args) {
1849     char **parts = g_strsplit(act, " ", 2);
1850     if (!parts) return;
1851     else if ((g_strcmp0(parts[0], "spawn") == 0)
1852              || (g_strcmp0(parts[0], "sh") == 0)
1853              || (g_strcmp0(parts[0], "sync_spawn") == 0)
1854              || (g_strcmp0(parts[0], "sync_sh") == 0)) {
1855         guint i;
1856         GString *a = g_string_new ("");
1857         char **spawnparts;
1858         spawnparts = split_quoted(parts[1], FALSE);
1859         g_string_append_printf(a, "%s", spawnparts[0]);
1860         if (args) g_string_append_printf(a, " %s", args); /* append handler args before user args */
1861         
1862         for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
1863             g_string_append_printf(a, " %s", spawnparts[i]);
1864         parse_command(parts[0], a->str);
1865         g_string_free (a, TRUE);
1866         g_strfreev (spawnparts);
1867     } else
1868         parse_command(parts[0], parts[1]);
1869     g_strfreev (parts);
1870 }
1871
1872 static void
1873 add_binding (const gchar *key, const gchar *act) {
1874     char **parts = g_strsplit(act, " ", 2);
1875     Action *action;
1876
1877     if (!parts)
1878         return;
1879
1880     //Debug:
1881     if (uzbl.state.verbose)
1882         printf ("Binding %-10s : %s\n", key, act);
1883     action = new_action(parts[0], parts[1]);
1884
1885     g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
1886     g_strfreev(parts);
1887 }
1888
1889 static gchar*
1890 get_xdg_var (XDG_Var xdg) {
1891     const gchar* actual_value = getenv (xdg.environmental);
1892     const gchar* home         = getenv ("HOME");
1893
1894     gchar* return_value = str_replace ("~", home, actual_value);
1895
1896     if (! actual_value || strcmp (actual_value, "") == 0) {
1897         if (xdg.default_value) {
1898             return_value = str_replace ("~", home, xdg.default_value);
1899         } else {
1900             return_value = NULL;
1901         }
1902     }
1903     return return_value;
1904 }
1905
1906 static gchar*
1907 find_xdg_file (int xdg_type, char* filename) {
1908     /* xdg_type = 0 => config
1909        xdg_type = 1 => data
1910        xdg_type = 2 => cache*/
1911
1912     gchar* xdgv = get_xdg_var (XDG[xdg_type]);
1913     gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
1914     g_free (xdgv);
1915
1916     gchar* temporary_string;
1917     char*  saveptr;
1918     char*  buf;
1919
1920     if (! file_exists (temporary_file) && xdg_type != 2) {
1921         buf = get_xdg_var (XDG[3 + xdg_type]);
1922         temporary_string = (char *) strtok_r (buf, ":", &saveptr);
1923         g_free(buf);
1924
1925         while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
1926             g_free (temporary_file);
1927             temporary_file = g_strconcat (temporary_string, filename, NULL);
1928         }
1929     }
1930     
1931     //g_free (temporary_string); - segfaults.
1932
1933     if (file_exists (temporary_file)) {
1934         return temporary_file;
1935     } else {
1936         return NULL;
1937     }
1938 }
1939 static void
1940 settings_init () {
1941     State *s = &uzbl.state;
1942     Network *n = &uzbl.net;
1943     int i;
1944     for (i = 0; default_config[i].command != NULL; i++) {
1945         parse_cmd_line(default_config[i].command);
1946     }
1947
1948     if (!s->config_file) {
1949         s->config_file = find_xdg_file (0, "/uzbl/config");
1950     }
1951
1952     if (s->config_file) {
1953         GArray* lines = read_file_by_line (s->config_file);
1954         int i = 0;
1955         gchar* line;
1956
1957         while ((line = g_array_index(lines, gchar*, i))) {
1958             parse_cmd_line (line);
1959             i ++;
1960             g_free (line);
1961         }
1962         g_array_free (lines, TRUE);
1963     } else {
1964         if (uzbl.state.verbose)
1965             printf ("No configuration file loaded.\n");
1966     }
1967
1968     g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1969 }
1970
1971 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1972     (void) session;
1973     (void) user_data;
1974     if (!uzbl.behave.cookie_handler) return;
1975
1976     soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1977     GString *s = g_string_new ("");
1978     SoupURI * soup_uri = soup_message_get_uri(msg);
1979     g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
1980     run_handler(uzbl.behave.cookie_handler, s->str);
1981
1982     if(uzbl.comm.sync_stdout)
1983         soup_message_headers_replace (msg->request_headers, "Cookie", uzbl.comm.sync_stdout);
1984     //printf("stdout: %s\n", uzbl.comm.sync_stdout);   // debugging
1985     if (uzbl.comm.sync_stdout) uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
1986         
1987     g_string_free(s, TRUE);
1988 }
1989
1990 static void
1991 save_cookies (SoupMessage *msg, gpointer user_data){
1992     (void) user_data;
1993     GSList *ck;
1994     char *cookie;
1995     for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1996         cookie = soup_cookie_to_set_cookie_header(ck->data);
1997         SoupURI * soup_uri = soup_message_get_uri(msg);
1998         GString *s = g_string_new ("");
1999         g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2000         run_handler(uzbl.behave.cookie_handler, s->str);
2001         g_free (cookie);
2002         g_string_free(s, TRUE);
2003     }
2004     g_slist_free(ck);
2005 }
2006
2007 /* --- WEBINSPECTOR --- */
2008 static void
2009 hide_window_cb(GtkWidget *widget, gpointer data) {
2010     (void) data;
2011
2012     gtk_widget_hide(widget);
2013 }
2014
2015 static WebKitWebView*
2016 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2017     (void) data;
2018     (void) page;
2019     (void) web_inspector;
2020     GtkWidget* scrolled_window;
2021     GtkWidget* new_web_view;
2022     GUI *g = &uzbl.gui;
2023
2024     g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2025     g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2026             G_CALLBACK(hide_window_cb), NULL);
2027
2028     gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2029     gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2030     gtk_widget_show(g->inspector_window);
2031
2032     scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2033     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2034             GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2035     gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2036     gtk_widget_show(scrolled_window);
2037
2038     new_web_view = webkit_web_view_new();
2039     gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2040
2041     return WEBKIT_WEB_VIEW(new_web_view);
2042 }
2043
2044 static gboolean
2045 inspector_show_window_cb (WebKitWebInspector* inspector){
2046     (void) inspector;
2047     gtk_widget_show(uzbl.gui.inspector_window);
2048     return TRUE;
2049 }
2050
2051 /* TODO: Add variables and code to make use of these functions */
2052 static gboolean
2053 inspector_close_window_cb (WebKitWebInspector* inspector){
2054     (void) inspector;
2055     return TRUE;
2056 }
2057
2058 static gboolean
2059 inspector_attach_window_cb (WebKitWebInspector* inspector){
2060     (void) inspector;
2061     return FALSE;
2062 }
2063
2064 static gboolean
2065 inspector_dettach_window_cb (WebKitWebInspector* inspector){
2066     (void) inspector;
2067     return FALSE;
2068 }
2069
2070 static gboolean
2071 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2072     (void) inspector;
2073     return FALSE;
2074 }
2075
2076 static gboolean
2077 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2078     (void) inspector;
2079     return FALSE;
2080 }
2081
2082 static void
2083 set_up_inspector() {
2084     GUI *g = &uzbl.gui;
2085     WebKitWebSettings *settings = webkit_web_settings_new();
2086     g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2087     webkit_web_view_set_settings(WEBKIT_WEB_VIEW(uzbl.gui.web_view), settings);
2088
2089
2090     uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2091     g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2092     g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2093     g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2094     g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2095     g_signal_connect (G_OBJECT (g->inspector), "dettach-window", G_CALLBACK (inspector_dettach_window_cb), NULL);
2096     g_signal_connect (G_OBJECT (g->inspector), "destroy", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2097
2098     g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2099 }
2100
2101
2102
2103 /** -- MAIN -- **/
2104 int
2105 main (int argc, char* argv[]) {
2106     gtk_init (&argc, &argv);
2107     if (!g_thread_supported ())
2108         g_thread_init (NULL);
2109     uzbl.state.executable_path = g_strdup(argv[0]);
2110     uzbl.state.selected_url = NULL;
2111     uzbl.state.searchtx = NULL;
2112
2113     GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2114     g_option_context_add_main_entries (context, entries, NULL);
2115     g_option_context_add_group (context, gtk_get_option_group (TRUE));
2116     g_option_context_parse (context, &argc, &argv, NULL);
2117     g_option_context_free(context);
2118     /* initialize hash table */
2119     uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2120
2121     uzbl.net.soup_session = webkit_get_default_session();
2122     uzbl.state.keycmd = g_string_new("");
2123
2124     if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2125         fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2126     if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2127         fprintf(stderr, "uzbl: error hooking SIGINT\n");
2128
2129     if(uname(&uzbl.state.unameinfo) == -1)
2130         g_printerr("Can't retrieve unameinfo.  Your useragent might appear wrong.\n");
2131
2132     uzbl.gui.sbar.progress_s = g_strdup("=");
2133     uzbl.gui.sbar.progress_u = g_strdup("ยท");
2134     uzbl.gui.sbar.progress_w = 10;
2135
2136     setup_regex();
2137     setup_scanner();
2138     commands_hash ();
2139     make_var_to_name_hash();
2140
2141     uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2142
2143     uzbl.gui.scrolled_win = create_browser();
2144     create_mainbar();
2145
2146     /* initial packing */
2147     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2148     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2149
2150     uzbl.gui.main_window = create_window ();
2151     gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2152
2153
2154     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2155     gtk_widget_show_all (uzbl.gui.main_window);
2156     uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2157
2158     if (uzbl.state.verbose) {
2159         printf("Uzbl start location: %s\n", argv[0]);
2160         printf("window_id %i\n",(int) uzbl.xwin);
2161         printf("pid %i\n", getpid ());
2162         printf("name: %s\n", uzbl.state.instance_name);
2163     }
2164
2165     uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2166     uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2167     uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2168     uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2169     gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2170
2171     settings_init ();
2172
2173     if (!uzbl.behave.show_status)
2174         gtk_widget_hide(uzbl.gui.mainbar);
2175     else
2176         update_title();
2177
2178     /* WebInspector */
2179     set_up_inspector();
2180
2181     create_stdin();
2182
2183     if(uzbl.state.uri) {
2184         GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
2185         g_array_append_val(a, uzbl.state.uri);
2186         load_uri (uzbl.gui.web_view, a);
2187         g_array_free (a, TRUE);
2188     }
2189
2190     gtk_main ();
2191     clean_up();
2192
2193     return EXIT_SUCCESS;
2194 }
2195
2196 /* vi: set et ts=4: */