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