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