Cleaned up after lexical scanner
[uzbl-mobile] / uzbl.c
1 /* -*- c-basic-offset: 4; -*- */
2 // Original code taken from the example webkit-gtk+ application. see notice below.
3 // Modified code is licensed under the GPL 3.  See LICENSE file.
4
5
6 /*
7  * Copyright (C) 2006, 2007 Apple Inc.
8  * Copyright (C) 2007 Alp Toker <alp@atoker.com>
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
20  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32
33 #define LENGTH(x) (sizeof x / sizeof x[0])
34 #define MAX_BINDINGS 256
35 #define _POSIX_SOURCE
36
37 #include <gtk/gtk.h>
38 #include <gdk/gdkx.h>
39 #include <gdk/gdkkeysyms.h>
40 #include <sys/socket.h>
41 #include <sys/stat.h>
42 #include <sys/types.h>
43 #include <sys/un.h>
44 #include <sys/utsname.h>
45 #include <sys/time.h>
46 #include <webkit/webkit.h>
47 #include <libsoup/soup.h>
48
49 #include <stdio.h>
50 #include <string.h>
51 #include <unistd.h>
52 #include <stdlib.h>
53 #include <errno.h>
54 #include <fcntl.h>
55 #include <signal.h>
56 #include "uzbl.h"
57 #include "config.h"
58
59 static Uzbl uzbl;
60 typedef void (*Command)(WebKitWebView*, GArray *argv);
61
62
63
64 /* commandline arguments (set initial values for the state variables) */
65 static const 
66 GOptionEntry entries[] =
67 {
68     { "uri",     'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
69         "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
70     { "verbose", 'v', 0, G_OPTION_ARG_NONE,   &uzbl.state.verbose,
71         "Whether to print all messages or just errors.", NULL },
72     { "name",    'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name, 
73         "Name of the current instance (defaults to Xorg window id)", "NAME" },
74     { "config",  'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,   
75         "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
76     { NULL,      0, 0, 0, NULL, NULL, NULL }
77 };
78
79 /* associate command names to their properties */
80 typedef const struct {
81     /* 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 static 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 static 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 static gchar*
249 strfree(gchar *str) { g_free(str); return NULL; }  // for freeing & setting to null in one go
250
251 static gchar*
252 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
253
254 static 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 static 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 static
293 gchar* 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 static 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 static 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 static 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 static void
363 catch_sigterm(int s) {
364     (void) s;
365     clean_up();
366 }
367
368 static void
369 catch_sigint(int s) {
370     (void) s;
371     clean_up();
372     exit(EXIT_SUCCESS);
373 }
374
375 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static void
472 scroll_vert(WebKitWebView* page, GArray *argv) {
473     (void) page;
474     scroll(uzbl.gui.bar_v, argv);
475 }
476
477 static void
478 scroll_horz(WebKitWebView* page, GArray *argv) {
479     (void) page;
480     scroll(uzbl.gui.bar_h, argv);
481 }
482
483 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static void
576 destroy_cb (GtkWidget* widget, gpointer data) {
577     (void) widget;
578     (void) data;
579     gtk_main_quit ();
580 }
581
582 static 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) static 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 static 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     { "search",             {search_forward_text, NOSPLIT} },
631     { "search_reverse",     {search_reverse_text, NOSPLIT} },
632     { "dehilight",          {dehilight, 0}                 },
633     { "toggle_insert_mode", {toggle_insert_mode, 0}        },
634     { "set",                {set_var, NOSPLIT}             },
635     //{ "get",                {get_var, NOSPLIT}             },
636     { "bind",               {act_bind, NOSPLIT}            },
637     { "dump_config",        {act_dump_config, 0}           },
638     { "keycmd",             {keycmd, NOSPLIT}              },
639     { "keycmd_nl",          {keycmd_nl, NOSPLIT}           },
640     { "keycmd_bs",          {keycmd_bs, 0}                 },
641     { "chain",              {chain, 0}                     },
642     { "print",              {print, NOSPLIT}               }
643 };
644
645 static void
646 commands_hash(void)
647 {
648     unsigned int i;
649     uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
650
651     for (i = 0; i < LENGTH(cmdlist); i++)
652         g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
653 }
654
655 /* -- CORE FUNCTIONS -- */
656
657 void
658 free_action(gpointer act) {
659     Action *action = (Action*)act;
660     g_free(action->name);
661     if (action->param)
662         g_free(action->param);
663     g_free(action);
664 }
665
666 Action*
667 new_action(const gchar *name, const gchar *param) {
668     Action *action = g_new(Action, 1);
669
670     action->name = g_strdup(name);
671     if (param)
672         action->param = g_strdup(param);
673     else
674         action->param = NULL;
675
676     return action;
677 }
678
679 static bool
680 file_exists (const char * filename) {
681     return (access(filename, F_OK) == 0);
682 }
683
684 static void
685 set_var(WebKitWebView *page, GArray *argv) {
686     (void) page;
687     gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
688     gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
689     set_var_value(g_strstrip(split[0]), value);
690     g_free(value);
691     g_strfreev(split);
692 }
693
694 static void
695 print(WebKitWebView *page, GArray *argv) {
696     (void) page;
697     gchar* buf;
698
699     buf = expand_vars(argv_idx(argv, 0));
700     puts(buf);
701     g_free(buf);
702 }
703
704 static void
705 act_bind(WebKitWebView *page, GArray *argv) {
706     (void) page;
707     gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
708     gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
709     add_binding(g_strstrip(split[0]), value);
710     g_free(value);
711     g_strfreev(split);
712 }
713
714
715 static void
716 act_dump_config() {
717     dump_config();
718 }
719
720 static void
721 toggle_insert_mode(WebKitWebView *page, GArray *argv) {
722     (void)page;
723
724     if (argv_idx(argv, 0)) {
725         if (strcmp (argv_idx(argv, 0), "0") == 0) {
726             uzbl.behave.insert_mode = FALSE;
727         } else {
728             uzbl.behave.insert_mode = TRUE;
729         }
730     } else {
731         uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
732     }
733
734     update_title();
735 }
736
737 static void
738 load_uri (WebKitWebView *web_view, GArray *argv) {
739     if (argv_idx(argv, 0)) {
740         GString* newuri = g_string_new (argv_idx(argv, 0));
741         if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
742             run_js(web_view, argv);
743             return;
744         }
745         if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
746             g_string_prepend (newuri, "http://");
747         /* if we do handle cookies, ask our handler for them */
748         webkit_web_view_load_uri (web_view, newuri->str);
749         g_string_free (newuri, TRUE);
750     }
751 }
752
753 static void
754 run_js (WebKitWebView * web_view, GArray *argv) {
755     if (argv_idx(argv, 0))
756         webkit_web_view_execute_script (web_view, argv_idx(argv, 0));
757 }
758
759 static void
760 run_external_js (WebKitWebView * web_view, GArray *argv) {
761     if (argv_idx(argv, 0)) {
762         GArray* lines = read_file_by_line (argv_idx (argv, 0));
763         gchar*  js = NULL;
764         int i = 0;
765         gchar* line;
766
767         while ((line = g_array_index(lines, gchar*, i))) {
768             if (js == NULL) {
769                 js = g_strdup (line);
770             } else {
771                 gchar* newjs = g_strconcat (js, line, NULL);
772                 js = newjs;
773             }
774             i ++;
775             g_free (line);
776         }
777         
778         if (uzbl.state.verbose)
779             printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
780
781         if (argv_idx (argv, 1)) {
782             gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
783             g_free (js);
784             js = newjs;
785         }
786         webkit_web_view_execute_script (web_view, js);
787         g_free (js);
788         g_array_free (lines, TRUE);
789     }
790 }
791
792 static void
793 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
794     if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
795         if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
796             webkit_web_view_unmark_text_matches (page);
797             webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
798             uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
799         }
800     }
801     
802     if (uzbl.state.searchtx) {
803         if (uzbl.state.verbose)
804             printf ("Searching: %s\n", uzbl.state.searchtx);
805         webkit_web_view_set_highlight_text_matches (page, TRUE);
806         webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
807     }
808 }
809
810 static void
811 search_forward_text (WebKitWebView *page, GArray *argv) {
812     search_text(page, argv, TRUE);
813 }
814
815 static void
816 search_reverse_text (WebKitWebView *page, GArray *argv) {
817     search_text(page, argv, FALSE);
818 }
819
820 static void
821 dehilight (WebKitWebView *page, GArray *argv) {
822     (void) argv;
823     webkit_web_view_set_highlight_text_matches (page, FALSE);
824 }
825
826
827 static void
828 new_window_load_uri (const gchar * uri) {
829     GString* to_execute = g_string_new ("");
830     g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
831     int i;
832     for (i = 0; entries[i].long_name != NULL; i++) {
833         if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
834             gchar** str = (gchar**)entries[i].arg_data;
835             if (*str!=NULL) {
836                 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
837             }
838         }
839     }
840     if (uzbl.state.verbose)
841         printf("\n%s\n", to_execute->str);
842     g_spawn_command_line_async (to_execute->str, NULL);
843     g_string_free (to_execute, TRUE);
844 }
845
846 static void
847 chain (WebKitWebView *page, GArray *argv) {
848     (void)page;
849     gchar *a = NULL;
850     gchar **parts = NULL;
851     guint i = 0;    
852     while ((a = argv_idx(argv, i++))) {
853         parts = g_strsplit (a, " ", 2);
854         parse_command(parts[0], parts[1]);
855         g_strfreev (parts);
856     }
857 }
858
859 static void
860 keycmd (WebKitWebView *page, GArray *argv) {
861     (void)page;
862     (void)argv;
863     g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
864     run_keycmd(FALSE);
865     update_title();
866 }
867
868 static void
869 keycmd_nl (WebKitWebView *page, GArray *argv) {
870     (void)page;
871     (void)argv;
872     g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
873     run_keycmd(TRUE);
874     update_title();
875 }
876
877 static void
878 keycmd_bs (WebKitWebView *page, GArray *argv) {
879     (void)page;
880     (void)argv;
881     g_string_truncate(uzbl.state.keycmd, 
882         /* Calculate the number of bytes to truncate...
883          * This is not simply (len-1) when dealing with UTF-8 string */
884         g_utf8_offset_to_pointer(uzbl.state.keycmd->str, 
885              g_utf8_strlen(uzbl.state.keycmd->str, uzbl.state.keycmd->len) - 1) 
886              - uzbl.state.keycmd->str);
887     update_title();
888 }
889
890 static void
891 close_uzbl (WebKitWebView *page, GArray *argv) {
892     (void)page;
893     (void)argv;
894     gtk_main_quit ();
895 }
896
897 /* --Statusbar functions-- */
898 static char*
899 build_progressbar_ascii(int percent) {
900    int width=uzbl.gui.sbar.progress_w;
901    int i;
902    double l;
903    GString *bar = g_string_new("");
904
905    l = (double)percent*((double)width/100.);
906    l = (int)(l+.5)>=(int)l ? l+.5 : l;
907
908    for(i=0; i<(int)l; i++)
909        g_string_append(bar, uzbl.gui.sbar.progress_s);
910
911    for(; i<width; i++)
912        g_string_append(bar, uzbl.gui.sbar.progress_u);
913
914    return g_string_free(bar, FALSE);
915 }
916
917 static void
918 setup_scanner() {
919      const GScannerConfig scan_config = {
920              (
921               "\t\r\n"
922              )            /* cset_skip_characters */,
923              (
924               G_CSET_a_2_z
925               "_#"
926               G_CSET_A_2_Z
927              )            /* cset_identifier_first */,
928              (
929               G_CSET_a_2_z
930               "_0123456789"
931               G_CSET_A_2_Z
932               G_CSET_LATINS
933               G_CSET_LATINC
934              )            /* cset_identifier_nth */,
935              ( "" )    /* cpair_comment_single */,
936
937              TRUE         /* case_sensitive */,
938
939              FALSE        /* skip_comment_multi */,
940              FALSE        /* skip_comment_single */,
941              FALSE        /* scan_comment_multi */,
942              TRUE         /* scan_identifier */,
943              TRUE         /* scan_identifier_1char */,
944              FALSE        /* scan_identifier_NULL */,
945              TRUE         /* scan_symbols */,
946              FALSE        /* scan_binary */,
947              FALSE        /* scan_octal */,
948              FALSE        /* scan_float */,
949              FALSE        /* scan_hex */,
950              FALSE        /* scan_hex_dollar */,
951              FALSE        /* scan_string_sq */,
952              FALSE        /* scan_string_dq */,
953              TRUE         /* numbers_2_int */,
954              FALSE        /* int_2_float */,
955              FALSE        /* identifier_2_string */,
956              FALSE        /* char_2_token */,
957              FALSE        /* symbol_2_token */,
958              TRUE         /* scope_0_fallback */,
959              FALSE,
960              TRUE
961      };
962
963      uzbl.scan = g_scanner_new(&scan_config);
964      while(symp->symbol_name) {
965          g_scanner_scope_add_symbol(uzbl.scan, 0,
966                          symp->symbol_name,
967                          GINT_TO_POINTER(symp->symbol_token));
968          symp++;
969      }
970 }
971
972 static gchar *
973 expand_template(const char *template, gboolean escape_markup) {
974      if(!template) return NULL;
975
976      GTokenType token = G_TOKEN_NONE;
977      GString *ret = g_string_new("");
978      char *buf=NULL;
979      int sym;
980
981      g_scanner_input_text(uzbl.scan, template, strlen(template));
982      while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
983          token = g_scanner_get_next_token(uzbl.scan);
984
985          if(token == G_TOKEN_SYMBOL) {
986              sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
987              switch(sym) {
988                  case SYM_URI:
989                      if(escape_markup) {
990                          buf = uzbl.state.uri ?
991                              g_markup_printf_escaped("%s", uzbl.state.uri) : g_strdup("");
992                          g_string_append(ret, buf);
993                          g_free(buf);
994                      }
995                      else
996                          g_string_append(ret, uzbl.state.uri ?
997                                  uzbl.state.uri : g_strdup(""));
998                      break;
999                  case SYM_LOADPRGS:
1000                      buf = itos(uzbl.gui.sbar.load_progress);
1001                      g_string_append(ret, buf);
1002                      g_free(buf);
1003                      break;
1004                  case SYM_LOADPRGSBAR:
1005                      buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1006                      g_string_append(ret, buf);
1007                      g_free(buf);
1008                      break;
1009                  case SYM_TITLE:
1010                      if(escape_markup) {
1011                          buf = uzbl.gui.main_title ?
1012                              g_markup_printf_escaped("%s", uzbl.gui.main_title) : g_strdup("");
1013                          g_string_append(ret, buf);
1014                          g_free(buf);
1015                      }
1016                      else
1017                          g_string_append(ret, uzbl.gui.main_title ?
1018                                  uzbl.gui.main_title : g_strdup(""));
1019                      break;
1020                  case SYM_SELECTED_URI:
1021                      if(escape_markup) {
1022                          buf = uzbl.state.selected_url ?
1023                              g_markup_printf_escaped("%s", uzbl.state.selected_url) : g_strdup("");
1024                          g_string_append(ret, buf);
1025                          g_free(buf);
1026                      }
1027                      else
1028                          g_string_append(ret, uzbl.state.selected_url ?
1029                                  uzbl.state.selected_url : g_strdup(""));
1030                      break;
1031                  case SYM_NAME:
1032                      buf = itos(uzbl.xwin);
1033                      g_string_append(ret,
1034                              uzbl.state.instance_name ? uzbl.state.instance_name : buf);
1035                      g_free(buf);
1036                      break;
1037                  case SYM_KEYCMD:
1038                      if(escape_markup) {
1039                          buf = uzbl.state.keycmd->str ?
1040                              g_markup_printf_escaped("%s", uzbl.state.keycmd->str) : g_strdup("");
1041                          g_string_append(ret, buf);
1042                          g_free(buf);
1043                      }
1044                      else
1045                          g_string_append(ret, uzbl.state.keycmd->str ?
1046                                  uzbl.state.keycmd->str : g_strdup(""));
1047                      break;
1048                  case SYM_MODE:
1049                      g_string_append(ret,
1050                              uzbl.behave.insert_mode ?
1051                              uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
1052                      break;
1053                  case SYM_MSG:
1054                      g_string_append(ret,
1055                              uzbl.gui.sbar.msg ? uzbl.gui.sbar.msg : "");
1056                      break;
1057                      /* useragent syms */
1058                  case SYM_WK_MAJ:
1059                      buf = itos(WEBKIT_MAJOR_VERSION);
1060                      g_string_append(ret, buf);
1061                      g_free(buf);
1062                      break;
1063                  case SYM_WK_MIN:
1064                      buf = itos(WEBKIT_MINOR_VERSION);
1065                      g_string_append(ret, buf);
1066                      g_free(buf);
1067                      break;
1068                  case SYM_WK_MIC:
1069                      buf = itos(WEBKIT_MICRO_VERSION);
1070                      g_string_append(ret, buf);
1071                      g_free(buf);
1072                      break;
1073                  case SYM_SYSNAME:
1074                      g_string_append(ret, uzbl.state.unameinfo.sysname);
1075                      break;
1076                  case SYM_NODENAME:
1077                      g_string_append(ret, uzbl.state.unameinfo.nodename);
1078                      break;
1079                  case SYM_KERNREL:
1080                      g_string_append(ret, uzbl.state.unameinfo.release);
1081                      break;
1082                  case SYM_KERNVER:
1083                      g_string_append(ret, uzbl.state.unameinfo.version);
1084                      break;
1085                  case SYM_ARCHSYS:
1086                      g_string_append(ret, uzbl.state.unameinfo.machine);
1087                      break;
1088                  case SYM_ARCHUZBL:
1089                      g_string_append(ret, ARCH);
1090                      break;
1091 #ifdef _GNU_SOURCE
1092                  case SYM_DOMAINNAME:
1093                      g_string_append(ret, uzbl.state.unameinfo.domainname);
1094                      break;
1095 #endif
1096                  case SYM_COMMIT:
1097                      g_string_append(ret, COMMIT);
1098                      break;
1099                  default:
1100                      break;
1101              }
1102          }
1103          else if(token == G_TOKEN_INT) {
1104              g_string_append_printf(ret, "%lu", g_scanner_cur_value(uzbl.scan).v_int);
1105          }
1106          else if(token == G_TOKEN_IDENTIFIER) {
1107              g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1108          }
1109          else if(token == G_TOKEN_CHAR) {
1110              g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1111          }
1112          else if(token == G_TOKEN_ERROR) {
1113              g_scanner_error(uzbl.scan, "Token error in template ('%s') at line %d, column %d.", 
1114                                         template, 
1115                                         g_scanner_cur_line(uzbl.scan), 
1116                                         g_scanner_cur_position(uzbl.scan));
1117          }
1118      }
1119
1120      return g_string_free(ret, FALSE);
1121 }
1122 /* --End Statusbar functions-- */
1123
1124 static void
1125 sharg_append(GArray *a, const gchar *str) {
1126     const gchar *s = (str ? str : "");
1127     g_array_append_val(a, s);
1128 }
1129
1130 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1131 static gboolean
1132 run_command (const gchar *command, const guint npre, const gchar **args,
1133              const gboolean sync, char **output_stdout) {
1134    //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1135     GError *err = NULL;
1136     
1137     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1138     gchar *pid = itos(getpid());
1139     gchar *xwin = itos(uzbl.xwin);
1140     guint i;
1141     sharg_append(a, command);
1142     for (i = 0; i < npre; i++) /* add n args before the default vars */
1143         sharg_append(a, args[i]);
1144     sharg_append(a, uzbl.state.config_file);
1145     sharg_append(a, pid);
1146     sharg_append(a, xwin);
1147     sharg_append(a, uzbl.comm.fifo_path);
1148     sharg_append(a, uzbl.comm.socket_path);
1149     sharg_append(a, uzbl.state.uri);
1150     sharg_append(a, uzbl.gui.main_title);
1151
1152     for (i = npre; i < g_strv_length((gchar**)args); i++)
1153         sharg_append(a, args[i]);
1154     
1155     gboolean result;
1156     if (sync) {
1157         if (*output_stdout) *output_stdout = strfree(*output_stdout);
1158         
1159         result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1160                               NULL, NULL, output_stdout, NULL, NULL, &err);
1161     } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1162                                   NULL, NULL, NULL, &err);
1163
1164     if (uzbl.state.verbose) {
1165         GString *s = g_string_new("spawned:");
1166         for (i = 0; i < (a->len); i++) {
1167             gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1168             g_string_append_printf(s, " %s", qarg);
1169             g_free (qarg);
1170         }
1171         g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1172         printf("%s\n", s->str);
1173         g_string_free(s, TRUE);
1174         if(output_stdout) {
1175             printf("Stdout: %s\n", *output_stdout);
1176         }
1177     }
1178     if (err) {
1179         g_printerr("error on run_command: %s\n", err->message);
1180         g_error_free (err);
1181     }
1182     g_free (pid);
1183     g_free (xwin);
1184     g_array_free (a, TRUE);
1185     return result;
1186 }
1187
1188 static gchar**
1189 split_quoted(const gchar* src, const gboolean unquote) {
1190     /* split on unquoted space, return array of strings;
1191        remove a layer of quotes and backslashes if unquote */
1192     if (!src) return NULL;
1193     
1194     gboolean dq = FALSE;
1195     gboolean sq = FALSE;
1196     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1197     GString *s = g_string_new ("");
1198     const gchar *p;
1199     gchar **ret;
1200     gchar *dup;
1201     for (p = src; *p != '\0'; p++) {
1202         if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1203         else if (*p == '\\') { g_string_append_c(s, *p++);
1204                                g_string_append_c(s, *p); }
1205         else if ((*p == '"') && unquote && !sq) dq = !dq;
1206         else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1207                                      dq = !dq; }
1208         else if ((*p == '\'') && unquote && !dq) sq = !sq;
1209         else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1210                                       sq = ! sq; }
1211         else if ((*p == ' ') && !dq && !sq) {
1212             dup = g_strdup(s->str);
1213             g_array_append_val(a, dup);
1214             g_string_truncate(s, 0);
1215         } else g_string_append_c(s, *p);
1216     }
1217     dup = g_strdup(s->str);
1218     g_array_append_val(a, dup);
1219     ret = (gchar**)a->data;
1220     g_array_free (a, FALSE);
1221     g_string_free (s, TRUE);
1222     return ret;
1223 }
1224
1225 static void
1226 spawn(WebKitWebView *web_view, GArray *argv) {
1227     (void)web_view;
1228     //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1229     if (argv_idx(argv, 0))
1230         run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1231 }
1232
1233 static void
1234 spawn_sync(WebKitWebView *web_view, GArray *argv) {
1235     (void)web_view;
1236     
1237     if (argv_idx(argv, 0))
1238         run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1239                     TRUE, &uzbl.comm.sync_stdout);
1240 }
1241
1242 static void
1243 spawn_sh(WebKitWebView *web_view, GArray *argv) {
1244     (void)web_view;
1245     if (!uzbl.behave.shell_cmd) {
1246         g_printerr ("spawn_sh: shell_cmd is not set!\n");
1247         return;
1248     }
1249     
1250     guint i;
1251     gchar *spacer = g_strdup("");
1252     g_array_insert_val(argv, 1, spacer);
1253     gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1254
1255     for (i = 1; i < g_strv_length(cmd); i++)
1256         g_array_prepend_val(argv, cmd[i]);
1257
1258     if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1259     g_free (spacer);
1260     g_strfreev (cmd);
1261 }
1262
1263 static void
1264 spawn_sh_sync(WebKitWebView *web_view, GArray *argv) {
1265     (void)web_view;
1266     if (!uzbl.behave.shell_cmd) {
1267         g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1268         return;
1269     }
1270     
1271     guint i;
1272     gchar *spacer = g_strdup("");
1273     g_array_insert_val(argv, 1, spacer);
1274     gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1275
1276     for (i = 1; i < g_strv_length(cmd); i++)
1277         g_array_prepend_val(argv, cmd[i]);
1278          
1279     if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1280                          TRUE, &uzbl.comm.sync_stdout);
1281     g_free (spacer);
1282     g_strfreev (cmd);
1283 }
1284
1285 static void
1286 parse_command(const char *cmd, const char *param) {
1287     Command *c;
1288
1289     if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1290
1291             guint i;
1292             gchar **par = split_quoted(param, TRUE);
1293             GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1294
1295             if (c[1] == NOSPLIT) { /* don't split */
1296                 sharg_append(a, param);
1297             } else if (par) {
1298                 for (i = 0; i < g_strv_length(par); i++)
1299                     sharg_append(a, par[i]);
1300             }
1301             c[0](uzbl.gui.web_view, a);
1302             g_strfreev (par);
1303             g_array_free (a, TRUE);
1304
1305     } else
1306         g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1307 }
1308
1309 static void
1310 set_proxy_url() {
1311     SoupURI *suri;
1312
1313     if(*uzbl.net.proxy_url == ' '
1314        || uzbl.net.proxy_url == NULL) {
1315         soup_session_remove_feature_by_type(uzbl.net.soup_session,
1316                 (GType) SOUP_SESSION_PROXY_URI);
1317     }
1318     else {
1319         suri = soup_uri_new(uzbl.net.proxy_url);
1320         g_object_set(G_OBJECT(uzbl.net.soup_session),
1321                 SOUP_SESSION_PROXY_URI,
1322                 suri, NULL);
1323         soup_uri_free(suri);
1324     }
1325     return;
1326 }
1327
1328 static void
1329 set_icon() {
1330     if(file_exists(uzbl.gui.icon)) {
1331         gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1332     } else {
1333         g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1334     }
1335 }
1336
1337 static void
1338 cmd_load_uri() {
1339     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1340     g_array_append_val (a, uzbl.state.uri);
1341     load_uri(uzbl.gui.web_view, a);
1342     g_array_free (a, TRUE);
1343 }
1344
1345 static void 
1346 cmd_always_insert_mode() {
1347     uzbl.behave.insert_mode =
1348         uzbl.behave.always_insert_mode ?  TRUE : FALSE;
1349     update_title();
1350 }
1351
1352 static void
1353 cmd_max_conns() {
1354     g_object_set(G_OBJECT(uzbl.net.soup_session),
1355             SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1356 }
1357
1358 static void
1359 cmd_max_conns_host() {
1360     g_object_set(G_OBJECT(uzbl.net.soup_session),
1361             SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1362 }
1363
1364 static void
1365 cmd_http_debug() {
1366     soup_session_remove_feature
1367         (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1368     /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1369     /*g_free(uzbl.net.soup_logger);*/
1370
1371     uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1372     soup_session_add_feature(uzbl.net.soup_session,
1373             SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1374 }
1375
1376 static WebKitWebSettings*
1377 view_settings() {
1378     return webkit_web_view_get_settings(uzbl.gui.web_view);
1379 }
1380
1381 static void
1382 cmd_font_size() {
1383     WebKitWebSettings *ws = view_settings();
1384     if (uzbl.behave.font_size > 0) {
1385         g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1386     }
1387     
1388     if (uzbl.behave.monospace_size > 0) {
1389         g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1390                       uzbl.behave.monospace_size, NULL);
1391     } else {
1392         g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1393                       uzbl.behave.font_size, NULL);
1394     }
1395 }
1396
1397 static void
1398 cmd_zoom_level() {
1399     webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1400 }
1401
1402 static void
1403 cmd_disable_plugins() {
1404     g_object_set (G_OBJECT(view_settings()), "enable-plugins", 
1405             !uzbl.behave.disable_plugins, NULL);
1406 }
1407
1408 static void
1409 cmd_disable_scripts() {
1410     g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1411             !uzbl.behave.disable_scripts, NULL);
1412 }
1413
1414 static void
1415 cmd_minimum_font_size() {
1416     g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1417             uzbl.behave.minimum_font_size, NULL);
1418 }
1419 static void
1420 cmd_autoload_img() {
1421     g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1422             uzbl.behave.autoload_img, NULL);
1423 }
1424
1425
1426 static void
1427 cmd_autoshrink_img() {
1428     g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1429             uzbl.behave.autoshrink_img, NULL);
1430 }
1431
1432
1433 static void
1434 cmd_enable_spellcheck() {
1435     g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1436             uzbl.behave.enable_spellcheck, NULL);
1437 }
1438
1439 static void
1440 cmd_enable_private() {
1441     g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1442             uzbl.behave.enable_private, NULL);
1443 }
1444
1445 static void
1446 cmd_print_bg() {
1447     g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1448             uzbl.behave.print_bg, NULL);
1449 }
1450
1451 static void 
1452 cmd_style_uri() {
1453     g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1454             uzbl.behave.style_uri, NULL);
1455 }
1456
1457 static void 
1458 cmd_resizable_txt() {
1459     g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1460             uzbl.behave.resizable_txt, NULL);
1461 }
1462
1463 static void 
1464 cmd_default_encoding() {
1465     g_object_set (G_OBJECT(view_settings()), "default-encoding",
1466             uzbl.behave.default_encoding, NULL);
1467 }
1468
1469 static void 
1470 cmd_enforce_96dpi() {
1471     g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1472             uzbl.behave.enforce_96dpi, NULL);
1473 }
1474
1475 static void 
1476 cmd_caret_browsing() {
1477     g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1478             uzbl.behave.caret_browsing, NULL);
1479 }
1480
1481 static void
1482 cmd_cookie_handler() {
1483     gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1484     /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1485     if ((g_strcmp0(split[0], "sh") == 0) ||
1486         (g_strcmp0(split[0], "spawn") == 0)) {
1487         g_free (uzbl.behave.cookie_handler);
1488         uzbl.behave.cookie_handler =
1489             g_strdup_printf("sync_%s %s", split[0], split[1]);
1490     }
1491     g_strfreev (split);
1492 }
1493
1494 static void
1495 cmd_fifo_dir() {
1496     uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1497 }
1498
1499 static void
1500 cmd_socket_dir() {
1501     uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1502 }
1503
1504 static void
1505 cmd_inject_html() {
1506     if(uzbl.behave.inject_html) {
1507         webkit_web_view_load_html_string (uzbl.gui.web_view,
1508                 uzbl.behave.inject_html, NULL);
1509     }
1510 }
1511
1512 static void
1513 cmd_modkey() {
1514     int i;
1515     char *buf;
1516
1517     buf = g_utf8_strup(uzbl.behave.modkey, -1);
1518     uzbl.behave.modmask = 0;
1519
1520     if(uzbl.behave.modkey) 
1521         g_free(uzbl.behave.modkey);
1522     uzbl.behave.modkey = buf;
1523
1524     for (i = 0; modkeys[i].key != NULL; i++) {
1525         if (g_strrstr(buf, modkeys[i].key))
1526             uzbl.behave.modmask |= modkeys[i].mask;
1527     }
1528 }
1529
1530 static void
1531 cmd_useragent() {
1532     if (*uzbl.net.useragent == ' ') {
1533         g_free (uzbl.net.useragent);
1534         uzbl.net.useragent = NULL;
1535     } else {
1536         gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1537         if (ua)
1538             g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1539         g_free(uzbl.net.useragent);
1540         uzbl.net.useragent = ua;
1541     }
1542 }
1543
1544 static void
1545 move_statusbar() {
1546     gtk_widget_ref(uzbl.gui.scrolled_win);
1547     gtk_widget_ref(uzbl.gui.mainbar);
1548     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1549     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1550
1551     if(uzbl.behave.status_top) {
1552         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1553         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1554     }
1555     else {
1556         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1557         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1558     }
1559     gtk_widget_unref(uzbl.gui.scrolled_win);
1560     gtk_widget_unref(uzbl.gui.mainbar);
1561     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1562     return;
1563 }
1564
1565 static gboolean
1566 set_var_value(gchar *name, gchar *val) {
1567     uzbl_cmdprop *c = NULL;
1568     char *endp = NULL;
1569     char *buf = NULL;
1570
1571     if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1572         /* check for the variable type */
1573         if (c->type == TYPE_STR) {
1574             buf = expand_vars(val);
1575             g_free(*c->ptr);
1576             *c->ptr = buf;
1577         } else if(c->type == TYPE_INT) {
1578             int *ip = (int *)c->ptr;
1579             buf = expand_vars(val);
1580             *ip = (int)strtoul(buf, &endp, 10);
1581             g_free(buf);
1582         } else if (c->type == TYPE_FLOAT) {
1583             float *fp = (float *)c->ptr;
1584             buf = expand_vars(val);
1585             *fp = strtod(buf, &endp);
1586             g_free(buf);
1587         }
1588
1589         /* invoke a command specific function */
1590         if(c->func) c->func();
1591     }
1592     return TRUE;
1593 }
1594
1595 static void
1596 render_html() {
1597     Behaviour *b = &uzbl.behave;
1598
1599     if(b->html_buffer->str) {
1600         webkit_web_view_load_html_string (uzbl.gui.web_view,
1601                 b->html_buffer->str, b->base_url);
1602         g_string_free(b->html_buffer, TRUE);
1603         b->html_buffer = g_string_new("");
1604     }
1605 }
1606
1607 enum {M_CMD, M_HTML};
1608 static void
1609 parse_cmd_line(const char *ctl_line) {
1610     Behaviour *b = &uzbl.behave;
1611     size_t len=0;
1612
1613     if(b->mode == M_HTML) {
1614         len = strlen(b->html_endmarker);
1615         /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1616         if(len == strlen(ctl_line)-1 &&
1617            !strncmp(b->html_endmarker, ctl_line, len)) {
1618             set_timeout(0);
1619             set_var_value("mode", "0");
1620             render_html();
1621             return;
1622         }
1623         else {
1624             set_timeout(b->html_timeout);
1625             g_string_append(b->html_buffer, ctl_line);
1626         }
1627     }
1628     else if((ctl_line[0] == '#') /* Comments */
1629             || (ctl_line[0] == ' ')
1630             || (ctl_line[0] == '\n'))
1631         ; /* ignore these lines */
1632     else { /* parse a command */
1633         gchar *ctlstrip;
1634         gchar **tokens = NULL;
1635         len = strlen(ctl_line);
1636
1637         if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1638             ctlstrip = g_strndup(ctl_line, len - 1);
1639         else ctlstrip = g_strdup(ctl_line);
1640
1641         tokens = g_strsplit(ctlstrip, " ", 2);
1642         parse_command(tokens[0], tokens[1]);
1643         g_free(ctlstrip);
1644         g_strfreev(tokens);
1645     }
1646 }
1647
1648 static gchar*
1649 build_stream_name(int type, const gchar* dir) {
1650     char *xwin_str;
1651     State *s = &uzbl.state;
1652     gchar *str;
1653
1654     xwin_str = itos((int)uzbl.xwin);
1655     if (type == FIFO) {
1656         str = g_strdup_printf
1657             ("%s/uzbl_fifo_%s", dir,
1658              s->instance_name ? s->instance_name : xwin_str);
1659     } else if (type == SOCKET) {
1660         str = g_strdup_printf
1661             ("%s/uzbl_socket_%s", dir,
1662              s->instance_name ? s->instance_name : xwin_str );
1663     }
1664     g_free(xwin_str);
1665     return str;
1666 }
1667
1668 static gboolean
1669 control_fifo(GIOChannel *gio, GIOCondition condition) {
1670     if (uzbl.state.verbose)
1671         printf("triggered\n");
1672     gchar *ctl_line;
1673     GIOStatus ret;
1674     GError *err = NULL;
1675
1676     if (condition & G_IO_HUP)
1677         g_error ("Fifo: Read end of pipe died!\n");
1678
1679     if(!gio)
1680        g_error ("Fifo: GIOChannel broke\n");
1681
1682     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1683     if (ret == G_IO_STATUS_ERROR) {
1684         g_error ("Fifo: Error reading: %s\n", err->message);
1685         g_error_free (err);
1686     }
1687
1688     parse_cmd_line(ctl_line);
1689     g_free(ctl_line);
1690
1691     return TRUE;
1692 }
1693
1694 static gchar*
1695 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1696     if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1697         if (unlink(uzbl.comm.fifo_path) == -1)
1698             g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1699         g_free(uzbl.comm.fifo_path);
1700         uzbl.comm.fifo_path = NULL;
1701     }
1702
1703     if (*dir == ' ') { /* space unsets the variable */
1704         g_free (dir);
1705         return NULL;
1706     }
1707
1708     GIOChannel *chan = NULL;
1709     GError *error = NULL;
1710     gchar *path = build_stream_name(FIFO, dir);
1711
1712     if (!file_exists(path)) {
1713         if (mkfifo (path, 0666) == 0) {
1714             // 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.
1715             chan = g_io_channel_new_file(path, "r+", &error);
1716             if (chan) {
1717                 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1718                     if (uzbl.state.verbose)
1719                         printf ("init_fifo: created successfully as %s\n", path);
1720                     uzbl.comm.fifo_path = path;
1721                     return dir;
1722                 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1723             } else g_warning ("init_fifo: can't open: %s\n", error->message);
1724         } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1725     } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1726
1727     /* if we got this far, there was an error; cleanup */
1728     if (error) g_error_free (error);
1729     g_free(dir);
1730     g_free(path);
1731     return NULL;
1732 }
1733
1734 static gboolean
1735 control_stdin(GIOChannel *gio, GIOCondition condition) {
1736     (void) condition;
1737     gchar *ctl_line = NULL;
1738     GIOStatus ret;
1739
1740     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1741     if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1742         return FALSE;
1743
1744     parse_cmd_line(ctl_line);
1745     g_free(ctl_line);
1746
1747     return TRUE;
1748 }
1749
1750 static void
1751 create_stdin () {
1752     GIOChannel *chan = NULL;
1753     GError *error = NULL;
1754
1755     chan = g_io_channel_unix_new(fileno(stdin));
1756     if (chan) {
1757         if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1758             g_error ("Stdin: could not add watch\n");
1759         } else {
1760             if (uzbl.state.verbose)
1761                 printf ("Stdin: watch added successfully\n");
1762         }
1763     } else {
1764         g_error ("Stdin: Error while opening: %s\n", error->message);
1765     }
1766     if (error) g_error_free (error);
1767 }
1768
1769 static gboolean
1770 control_socket(GIOChannel *chan) {
1771     struct sockaddr_un remote;
1772     char buffer[512], *ctl_line;
1773     char temp[128];
1774     int sock, clientsock, n, done;
1775     unsigned int t;
1776
1777     sock = g_io_channel_unix_get_fd(chan);
1778
1779     memset (buffer, 0, sizeof (buffer));
1780
1781     t          = sizeof (remote);
1782     clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1783
1784     done = 0;
1785     do {
1786         memset (temp, 0, sizeof (temp));
1787         n = recv (clientsock, temp, 128, 0);
1788         if (n == 0) {
1789             buffer[strlen (buffer)] = '\0';
1790             done = 1;
1791         }
1792         if (!done)
1793             strcat (buffer, temp);
1794     } while (!done);
1795
1796     if (strcmp (buffer, "\n") < 0) {
1797         buffer[strlen (buffer) - 1] = '\0';
1798     } else {
1799         buffer[strlen (buffer)] = '\0';
1800     }
1801     close (clientsock);
1802     ctl_line = g_strdup(buffer);
1803     parse_cmd_line (ctl_line);
1804
1805 /*
1806    TODO: we should be able to do it with this.  but glib errors out with "Invalid argument"
1807     GError *error = NULL;
1808     gsize len;
1809     GIOStatus ret;
1810     ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1811     if (ret == G_IO_STATUS_ERROR)
1812         g_error ("Error reading: %s\n", error->message);
1813
1814     printf("Got line %s (%u bytes) \n",ctl_line, len);
1815     if(ctl_line) {
1816        parse_line(ctl_line);
1817 */
1818
1819     g_free(ctl_line);
1820     return TRUE;
1821 }
1822
1823 static gchar*
1824 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1825     if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1826         if (unlink(uzbl.comm.socket_path) == -1)
1827             g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1828         g_free(uzbl.comm.socket_path);
1829         uzbl.comm.socket_path = NULL;
1830     }
1831
1832     if (*dir == ' ') {
1833         g_free(dir);
1834         return NULL;
1835     }
1836
1837     GIOChannel *chan = NULL;
1838     int sock, len;
1839     struct sockaddr_un local;
1840     gchar *path = build_stream_name(SOCKET, dir);
1841
1842     sock = socket (AF_UNIX, SOCK_STREAM, 0);
1843
1844     local.sun_family = AF_UNIX;
1845     strcpy (local.sun_path, path);
1846     unlink (local.sun_path);
1847
1848     len = strlen (local.sun_path) + sizeof (local.sun_family);
1849     if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1850         if (uzbl.state.verbose)
1851             printf ("init_socket: opened in %s\n", path);
1852         listen (sock, 5);
1853
1854         if( (chan = g_io_channel_unix_new(sock)) ) {
1855             g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1856             uzbl.comm.socket_path = path;
1857             return dir;
1858         }
1859     } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1860
1861     /* if we got this far, there was an error; cleanup */
1862     g_free(path);
1863     g_free(dir);
1864     return NULL;
1865 }
1866
1867 /*
1868  NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1869  it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1870 */
1871 // this function may be called very early when the templates are not set (yet), hence the checks
1872 static void
1873 update_title (void) {
1874     Behaviour *b = &uzbl.behave;
1875     gchar *parsed;
1876
1877     if (b->show_status) {
1878         if (b->title_format_short) {
1879             parsed = expand_template(b->title_format_short, FALSE);
1880             gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1881             g_free(parsed);
1882         }
1883         if (b->status_format) {
1884             parsed = expand_template(b->status_format, TRUE);
1885             gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1886             g_free(parsed);
1887         }
1888         if (b->status_background) {
1889             GdkColor color;
1890             gdk_color_parse (b->status_background, &color);
1891             //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)
1892             gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1893         }
1894     } else {
1895         if (b->title_format_long) {
1896             parsed = expand_template(b->title_format_long, FALSE);
1897             gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1898             g_free(parsed);
1899         }
1900     }
1901 }
1902
1903 static gboolean
1904 key_press_cb (GtkWidget* window, GdkEventKey* event)
1905 {
1906     //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1907
1908     (void) window;
1909
1910     if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1911         || 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)
1912         return FALSE;
1913
1914     /* turn off insert mode (if always_insert_mode is not used) */
1915     if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1916         uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1917         update_title();
1918         return TRUE;
1919     }
1920
1921     if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1922         return FALSE;
1923
1924     if (event->keyval == GDK_Escape) {
1925         g_string_truncate(uzbl.state.keycmd, 0);
1926         update_title();
1927         dehilight(uzbl.gui.web_view, NULL);
1928         return TRUE;
1929     }
1930
1931     //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1932     if (event->keyval == GDK_Insert) {
1933         gchar * str;
1934         if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1935             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1936         } else {
1937             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1938         }
1939         if (str) {
1940             g_string_append (uzbl.state.keycmd, str);
1941             update_title ();
1942             g_free (str);
1943         }
1944         return TRUE;
1945     }
1946
1947     if (event->keyval == GDK_BackSpace)
1948         keycmd_bs(NULL, NULL);
1949
1950     gboolean key_ret = FALSE;
1951     if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1952         key_ret = TRUE;
1953     if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1954
1955     run_keycmd(key_ret);
1956     update_title();
1957     if (key_ret) return (!uzbl.behave.insert_mode);
1958     return TRUE;
1959 }
1960
1961 static void
1962 run_keycmd(const gboolean key_ret) {
1963     /* run the keycmd immediately if it isn't incremental and doesn't take args */
1964     Action *act;
1965     if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1966         g_string_truncate(uzbl.state.keycmd, 0);
1967         parse_command(act->name, act->param);
1968         return;
1969     }
1970
1971     /* try if it's an incremental keycmd or one that takes args, and run it */
1972     GString* short_keys = g_string_new ("");
1973     GString* short_keys_inc = g_string_new ("");
1974     guint i;
1975     for (i=0; i<(uzbl.state.keycmd->len); i++) {
1976         g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1977         g_string_assign(short_keys_inc, short_keys->str);
1978         g_string_append_c(short_keys, '_');
1979         g_string_append_c(short_keys_inc, '*');
1980
1981         if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1982             /* run normal cmds only if return was pressed */
1983             exec_paramcmd(act, i);
1984             g_string_truncate(uzbl.state.keycmd, 0);
1985             break;
1986         } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1987             if (key_ret)  /* just quit the incremental command on return */
1988                 g_string_truncate(uzbl.state.keycmd, 0);
1989             else exec_paramcmd(act, i); /* otherwise execute the incremental */
1990             break;
1991         }
1992         
1993         g_string_truncate(short_keys, short_keys->len - 1);
1994     }
1995     g_string_free (short_keys, TRUE);
1996     g_string_free (short_keys_inc, TRUE);
1997 }
1998
1999 static void
2000 exec_paramcmd(const Action *act, const guint i) {
2001     GString *parampart = g_string_new (uzbl.state.keycmd->str);
2002     GString *actionname = g_string_new ("");
2003     GString *actionparam = g_string_new ("");
2004     g_string_erase (parampart, 0, i+1);
2005     if (act->name)
2006         g_string_printf (actionname, act->name, parampart->str);
2007     if (act->param)
2008         g_string_printf (actionparam, act->param, parampart->str);
2009     parse_command(actionname->str, actionparam->str);
2010     g_string_free(actionname, TRUE);
2011     g_string_free(actionparam, TRUE);
2012     g_string_free(parampart, TRUE);
2013 }
2014
2015
2016 static GtkWidget*
2017 create_browser () {
2018     GUI *g = &uzbl.gui;
2019
2020     GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2021     //main_window_ref = g_object_ref(scrolled_window);
2022     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
2023
2024     g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2025     gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2026
2027     g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
2028     g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2029     g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2030     g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2031     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2032     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2033     g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2034     g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2035     g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2036     g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2037     g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2038
2039     return scrolled_window;
2040 }
2041
2042 static GtkWidget*
2043 create_mainbar () {
2044     GUI *g = &uzbl.gui;
2045
2046     g->mainbar = gtk_hbox_new (FALSE, 0);
2047
2048     /* keep a reference to the bar so we can re-pack it at runtime*/
2049     //sbar_ref = g_object_ref(g->mainbar);
2050
2051     g->mainbar_label = gtk_label_new ("");
2052     gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2053     gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2054     gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2055     gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2056     gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2057     g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2058     return g->mainbar;
2059 }
2060
2061 static
2062 GtkWidget* create_window () {
2063     GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2064     gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2065     gtk_widget_set_name (window, "Uzbl browser");
2066     g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2067     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2068
2069     return window;
2070 }
2071
2072 static gchar**
2073 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2074     /*
2075       If actname is one that calls an external command, this function will inject
2076       newargs in front of the user-provided args in that command line.  They will
2077       come become after the body of the script (in sh) or after the name of
2078       the command to execute (in spawn).
2079       i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2080       span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2081
2082       The return value consist of two strings: the action (sh, ...) and its args.
2083
2084       If act is not one that calls an external command, then the given action merely
2085       gets duplicated.
2086     */
2087     GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2088     gchar *actdup = g_strdup(actname);
2089     g_array_append_val(rets, actdup);
2090
2091     if ((g_strcmp0(actname, "spawn") == 0) ||
2092         (g_strcmp0(actname, "sh") == 0) ||
2093         (g_strcmp0(actname, "sync_spawn") == 0) ||
2094         (g_strcmp0(actname, "sync_sh") == 0)) {
2095         guint i;
2096         GString *a = g_string_new("");
2097         gchar **spawnparts = split_quoted(origargs, FALSE);
2098         g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2099         if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2100
2101         for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2102             if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2103
2104         g_array_append_val(rets, a->str);
2105         g_string_free(a, FALSE);
2106         g_strfreev(spawnparts);
2107     } else {
2108         gchar *origdup = g_strdup(origargs);
2109         g_array_append_val(rets, origdup);
2110     }
2111     return (gchar**)g_array_free(rets, FALSE);
2112 }
2113
2114 static void
2115 run_handler (const gchar *act, const gchar *args) {
2116     /* Consider this code a temporary hack to make the handlers usable.
2117        In practice, all this splicing, injection, and reconstruction is
2118        inefficient, annoying and hard to manage.  Potential pitfalls arise
2119        when the handler specific args 1) are not quoted  (the handler
2120        callbacks should take care of this)  2) are quoted but interfere
2121        with the users' own quotation.  A more ideal solution is
2122        to refactor parse_command so that it doesn't just take a string
2123        and execute it; rather than that, we should have a function which
2124        returns the argument vector parsed from the string.  This vector
2125        could be modified (e.g. insert additional args into it) before
2126        passing it to the next function that actually executes it.  Though
2127        it still isn't perfect for chain actions..  will reconsider & re-
2128        factor when I have the time. -duc */
2129
2130     char **parts = g_strsplit(act, " ", 2);
2131     if (!parts) return;
2132     if (g_strcmp0(parts[0], "chain") == 0) {
2133         GString *newargs = g_string_new("");
2134         gchar **chainparts = split_quoted(parts[1], FALSE);
2135         
2136         /* for every argument in the chain, inject the handler args
2137            and make sure the new parts are wrapped in quotes */
2138         gchar **cp = chainparts;
2139         gchar quot = '\'';
2140         gchar *quotless = NULL;
2141         gchar **spliced_quotless = NULL; // sigh -_-;
2142         gchar **inpart = NULL;
2143         
2144         while (*cp) {
2145             if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2146                 quot = **cp;
2147                 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2148             } else quotless = g_strdup(*cp);
2149
2150             spliced_quotless = g_strsplit(quotless, " ", 2);
2151             inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2152             g_strfreev(spliced_quotless);
2153             
2154             g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2155             g_free(quotless);
2156             g_strfreev(inpart);
2157             cp++;
2158         }
2159
2160         parse_command(parts[0], &(newargs->str[1]));
2161         g_string_free(newargs, TRUE);
2162         g_strfreev(chainparts);
2163         
2164     } else {
2165         gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2166         parse_command(inparts[0], inparts[1]);
2167         g_free(inparts[0]);
2168         g_free(inparts[1]);
2169     }
2170     g_strfreev(parts);
2171 }
2172
2173 static void
2174 add_binding (const gchar *key, const gchar *act) {
2175     char **parts = g_strsplit(act, " ", 2);
2176     Action *action;
2177
2178     if (!parts)
2179         return;
2180
2181     //Debug:
2182     if (uzbl.state.verbose)
2183         printf ("Binding %-10s : %s\n", key, act);
2184     action = new_action(parts[0], parts[1]);
2185
2186     if (g_hash_table_remove (uzbl.bindings, key))
2187         g_warning ("Overwriting existing binding for \"%s\"", key);
2188     g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2189     g_strfreev(parts);
2190 }
2191
2192 static gchar*
2193 get_xdg_var (XDG_Var xdg) {
2194     const gchar* actual_value = getenv (xdg.environmental);
2195     const gchar* home         = getenv ("HOME");
2196     gchar* return_value;
2197
2198     if (! actual_value || strcmp (actual_value, "") == 0) {
2199         if (xdg.default_value) {
2200             return_value = str_replace ("~", home, xdg.default_value);
2201         } else {
2202             return_value = NULL;
2203         }
2204     } else {
2205         return_value = str_replace("~", home, actual_value);
2206     }
2207
2208     return return_value;
2209 }
2210
2211 static gchar*
2212 find_xdg_file (int xdg_type, char* filename) {
2213     /* xdg_type = 0 => config
2214        xdg_type = 1 => data
2215        xdg_type = 2 => cache*/
2216
2217     gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2218     gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2219     g_free (xdgv);
2220
2221     gchar* temporary_string;
2222     char*  saveptr;
2223     char*  buf;
2224
2225     if (! file_exists (temporary_file) && xdg_type != 2) {
2226         buf = get_xdg_var (XDG[3 + xdg_type]);
2227         temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2228         g_free(buf);
2229
2230         while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2231             g_free (temporary_file);
2232             temporary_file = g_strconcat (temporary_string, filename, NULL);
2233         }
2234     }
2235     
2236     //g_free (temporary_string); - segfaults.
2237
2238     if (file_exists (temporary_file)) {
2239         return temporary_file;
2240     } else {
2241         return NULL;
2242     }
2243 }
2244 static void
2245 settings_init () {
2246     State *s = &uzbl.state;
2247     Network *n = &uzbl.net;
2248     int i;
2249     for (i = 0; default_config[i].command != NULL; i++) {
2250         parse_cmd_line(default_config[i].command);
2251     }
2252
2253     if (!s->config_file) {
2254         s->config_file = find_xdg_file (0, "/uzbl/config");
2255     }
2256
2257     if (s->config_file) {
2258         GArray* lines = read_file_by_line (s->config_file);
2259         int i = 0;
2260         gchar* line;
2261
2262         while ((line = g_array_index(lines, gchar*, i))) {
2263             parse_cmd_line (line);
2264             i ++;
2265             g_free (line);
2266         }
2267         g_array_free (lines, TRUE);
2268     } else {
2269         if (uzbl.state.verbose)
2270             printf ("No configuration file loaded.\n");
2271     }
2272
2273     g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2274 }
2275
2276 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2277     (void) session;
2278     (void) user_data;
2279     if (!uzbl.behave.cookie_handler)
2280          return;
2281
2282     soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2283     GString *s = g_string_new ("");
2284     SoupURI * soup_uri = soup_message_get_uri(msg);
2285     g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2286     run_handler(uzbl.behave.cookie_handler, s->str);
2287
2288     if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2289         char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2290         if ( p != NULL ) *p = '\0';
2291         soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2292     }
2293     if (uzbl.comm.sync_stdout)
2294         uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2295         
2296     g_string_free(s, TRUE);
2297 }
2298
2299 static void
2300 save_cookies (SoupMessage *msg, gpointer user_data){
2301     (void) user_data;
2302     GSList *ck;
2303     char *cookie;
2304     for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2305         cookie = soup_cookie_to_set_cookie_header(ck->data);
2306         SoupURI * soup_uri = soup_message_get_uri(msg);
2307         GString *s = g_string_new ("");
2308         g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2309         run_handler(uzbl.behave.cookie_handler, s->str);
2310         g_free (cookie);
2311         g_string_free(s, TRUE);
2312     }
2313     g_slist_free(ck);
2314 }
2315
2316 /* --- WEBINSPECTOR --- */
2317 static void
2318 hide_window_cb(GtkWidget *widget, gpointer data) {
2319     (void) data;
2320
2321     gtk_widget_hide(widget);
2322 }
2323
2324 static WebKitWebView*
2325 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2326     (void) data;
2327     (void) page;
2328     (void) web_inspector;
2329     GtkWidget* scrolled_window;
2330     GtkWidget* new_web_view;
2331     GUI *g = &uzbl.gui;
2332
2333     g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2334     g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2335             G_CALLBACK(hide_window_cb), NULL);
2336
2337     gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2338     gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2339     gtk_widget_show(g->inspector_window);
2340
2341     scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2342     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2343             GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2344     gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2345     gtk_widget_show(scrolled_window);
2346
2347     new_web_view = webkit_web_view_new();
2348     gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2349
2350     return WEBKIT_WEB_VIEW(new_web_view);
2351 }
2352
2353 static gboolean
2354 inspector_show_window_cb (WebKitWebInspector* inspector){
2355     (void) inspector;
2356     gtk_widget_show(uzbl.gui.inspector_window);
2357     return TRUE;
2358 }
2359
2360 /* TODO: Add variables and code to make use of these functions */
2361 static gboolean
2362 inspector_close_window_cb (WebKitWebInspector* inspector){
2363     (void) inspector;
2364     return TRUE;
2365 }
2366
2367 static gboolean
2368 inspector_attach_window_cb (WebKitWebInspector* inspector){
2369     (void) inspector;
2370     return FALSE;
2371 }
2372
2373 static gboolean
2374 inspector_detach_window_cb (WebKitWebInspector* inspector){
2375     (void) inspector;
2376     return FALSE;
2377 }
2378
2379 static gboolean
2380 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2381     (void) inspector;
2382     return FALSE;
2383 }
2384
2385 static gboolean
2386 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2387     (void) inspector;
2388     return FALSE;
2389 }
2390
2391 static void
2392 set_up_inspector() {
2393     GUI *g = &uzbl.gui;
2394     WebKitWebSettings *settings = view_settings();
2395     g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2396
2397     uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2398     g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2399     g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2400     g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2401     g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2402     g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2403     g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2404
2405     g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2406 }
2407
2408 static void
2409 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2410     (void) ud;
2411     uzbl_cmdprop *c = v;
2412
2413     if(!c->dump)
2414         return;
2415
2416     if(c->type == TYPE_STR)
2417         printf("set %s = %s\n", (char *)k, *c->ptr ? (char *)*c->ptr : " ");
2418     else if(c->type == TYPE_INT)
2419         printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2420     else if(c->type == TYPE_FLOAT)
2421         printf("set %s = %f\n", (char *)k, *(float *)c->ptr);
2422 }
2423
2424 static void
2425 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2426     (void) ud;
2427     Action *a = v;
2428
2429     printf("bind %s = %s %s\n", (char *)k ,
2430             (char *)a->name, a->param?(char *)a->param:"");
2431 }
2432
2433 static void
2434 dump_config() {
2435     g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2436     g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2437 }
2438
2439 /** -- MAIN -- **/
2440 int
2441 main (int argc, char* argv[]) {
2442     gtk_init (&argc, &argv);
2443     if (!g_thread_supported ())
2444         g_thread_init (NULL);
2445     uzbl.state.executable_path = g_strdup(argv[0]);
2446     uzbl.state.selected_url = NULL;
2447     uzbl.state.searchtx = NULL;
2448
2449     GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2450     g_option_context_add_main_entries (context, entries, NULL);
2451     g_option_context_add_group (context, gtk_get_option_group (TRUE));
2452     g_option_context_parse (context, &argc, &argv, NULL);
2453     g_option_context_free(context);
2454     
2455     gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2456     gboolean verbose_override = uzbl.state.verbose;
2457
2458     /* initialize hash table */
2459     uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2460
2461     uzbl.net.soup_session = webkit_get_default_session();
2462     uzbl.state.keycmd = g_string_new("");
2463
2464     if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2465         fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2466     if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2467         fprintf(stderr, "uzbl: error hooking SIGINT\n");
2468     if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2469         fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2470
2471
2472     if(uname(&uzbl.state.unameinfo) == -1)
2473         g_printerr("Can't retrieve unameinfo.  Your useragent might appear wrong.\n");
2474
2475     uzbl.gui.sbar.progress_s = g_strdup("=");
2476     uzbl.gui.sbar.progress_u = g_strdup("ยท");
2477     uzbl.gui.sbar.progress_w = 10;
2478
2479     /* HTML mode defaults*/
2480     uzbl.behave.html_buffer = g_string_new("");
2481     uzbl.behave.html_endmarker = g_strdup(".");
2482     uzbl.behave.html_timeout = 60;
2483     uzbl.behave.base_url = g_strdup("http://invalid");
2484
2485     /* default mode indicators */
2486     uzbl.behave.insert_indicator = g_strdup("I");
2487     uzbl.behave.cmd_indicator    = g_strdup("C");
2488
2489     setup_scanner();
2490     commands_hash ();
2491     make_var_to_name_hash();
2492
2493     uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2494
2495     uzbl.gui.scrolled_win = create_browser();
2496     create_mainbar();
2497
2498     /* initial packing */
2499     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2500     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2501
2502     uzbl.gui.main_window = create_window ();
2503     gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2504
2505
2506     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2507     gtk_widget_show_all (uzbl.gui.main_window);
2508     uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2509
2510     if (uzbl.state.verbose) {
2511         printf("Uzbl start location: %s\n", argv[0]);
2512         printf("window_id %i\n",(int) uzbl.xwin);
2513         printf("pid %i\n", getpid ());
2514         printf("name: %s\n", uzbl.state.instance_name);
2515     }
2516
2517     uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2518     uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2519     uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2520     uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2521     gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2522
2523     settings_init ();
2524
2525     if (!uzbl.behave.show_status)
2526         gtk_widget_hide(uzbl.gui.mainbar);
2527     else
2528         update_title();
2529
2530     /* WebInspector */
2531     set_up_inspector();
2532
2533     create_stdin();
2534
2535     if (verbose_override > uzbl.state.verbose)
2536         uzbl.state.verbose = verbose_override;
2537     
2538     if (uri_override) {
2539         set_var_value("uri", uri_override);
2540         g_free(uri_override);
2541     } else if (uzbl.state.uri)
2542         cmd_load_uri(uzbl.gui.web_view, NULL);
2543
2544     gtk_main ();
2545     clean_up();
2546
2547     return EXIT_SUCCESS;
2548 }
2549
2550 /* vi: set et ts=4: */