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