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