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