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