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