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