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