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