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