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