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