fix callback
[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 <webkit/webkit.h>
45 #include <stdio.h>
46 #include <string.h>
47 #include <unistd.h>
48 #include <stdlib.h>
49 #include <errno.h>
50 #include <string.h>
51 #include <fcntl.h>
52 #include <sys/socket.h>
53 #include <sys/un.h>
54 #include <libsoup/soup.h>
55 #include <signal.h>
56 #include "uzbl.h"
57
58
59 static Uzbl uzbl;
60
61 /* define names and pointers to all config specific variables */
62 typedef const struct {
63     void **ptr;
64     int is_string;
65     void (*func)(void);
66 } uzbl_cmdprop;
67
68 const struct {
69     char *name;
70     uzbl_cmdprop cp;
71 } var_name_to_ptr[] = {
72     { "uri",                {.ptr = (void *)&uzbl.state.uri,                   .is_string = 1,  .func = cmd_load_uri}},
73     { "status_message",     {.ptr = (void *)&uzbl.gui.sbar.msg,                .is_string = 1,  .func = update_title}},
74     { "show_status",        {.ptr = (void *)&uzbl.behave.show_status,          .is_string = 0,  .func = cmd_set_status}},
75     { "status_top",         {.ptr = (void *)&uzbl.behave.status_top,           .is_string = 0,  .func = move_statusbar}},
76     { "status_format",      {.ptr = (void *)&uzbl.behave.status_format,        .is_string = 1,  .func = update_title}},
77     { "status_background",  {.ptr = (void *)&uzbl.behave.status_background,    .is_string = 1,  .func = update_title}},
78     { "title_format_long",  {.ptr = (void *)&uzbl.behave.title_format_long,    .is_string = 1,  .func = update_title}},
79     { "title_format_short", {.ptr = (void *)&uzbl.behave.title_format_short,   .is_string = 1,  .func = update_title}},
80     { "insert_mode",        {.ptr = (void *)&uzbl.behave.insert_mode,          .is_string = 0,  .func = NULL}},
81     { "always_insert_mode", {.ptr = (void *)&uzbl.behave.always_insert_mode,   .is_string = 0,  .func = NULL}},
82     { "reset_command_mode", {.ptr = (void *)&uzbl.behave.reset_command_mode,   .is_string = 0,  .func = NULL}},
83     { "modkey"     ,        {.ptr = (void *)&uzbl.behave.modkey,               .is_string = 0,  .func = NULL}},
84     { "load_finish_handler",{.ptr = (void *)&uzbl.behave.load_finish_handler,  .is_string = 1,  .func = update_title}},
85     { "load_start_handler", {.ptr = (void *)&uzbl.behave.load_start_handler,   .is_string = 1,  .func = update_title}},
86     { "load_commit_handler",{.ptr = (void *)&uzbl.behave.load_commit_handler,  .is_string = 1,  .func = update_title}},
87     { "history_handler",    {.ptr = (void *)&uzbl.behave.history_handler,      .is_string = 1,  .func = update_title}},
88     { "download_handler",   {.ptr = (void *)&uzbl.behave.download_handler,     .is_string = 1,  .func = update_title}},
89     { "cookie_handler",     {.ptr = (void *)&uzbl.behave.cookie_handler,       .is_string = 1,  .func = update_title}},
90     { "fifo_dir",           {.ptr = (void *)&uzbl.behave.fifo_dir,             .is_string = 1,  .func = NULL}},
91     { "socket_dir",         {.ptr = (void *)&uzbl.behave.socket_dir,           .is_string = 1,  .func = NULL}},
92     { "http_debug",         {.ptr = (void *)&uzbl.behave.http_debug,           .is_string = 0,  .func = NULL}},
93     { "default_font_size",  {.ptr = (void *)&uzbl.behave.default_font_size,    .is_string = 0,  .func = NULL}},
94     { "minimum_font_size",  {.ptr = (void *)&uzbl.behave.minimum_font_size,    .is_string = 0,  .func = NULL}},
95     { "shell_cmd",          {.ptr = (void *)&uzbl.behave.shell_cmd,            .is_string = 1,  .func = NULL}},
96     { "proxy_url",          {.ptr = (void *)&uzbl.net.proxy_url,               .is_string = 1,  .func = set_proxy_url}},
97     { "max_conns",          {.ptr = (void *)&uzbl.net.max_conns,               .is_string = 1,  .func = NULL}},
98     { "max_conns_host",     {.ptr = (void *)&uzbl.net.max_conns_host,          .is_string = 0,  .func = NULL}},
99     { "useragent",          {.ptr = (void *)&uzbl.net.useragent,               .is_string = 1,  .func = NULL}},
100     { NULL,                 {.ptr = NULL,                                      .is_string = 0,  .func = NULL}}
101 }, *n2v_p = var_name_to_ptr;
102
103 const struct {
104     char *key;
105     guint mask;
106 } modkeys[] = {
107     { "SHIFT",   GDK_SHIFT_MASK   }, // shift
108     { "LOCK",    GDK_LOCK_MASK    }, // capslock or shiftlock, depending on xserver's modmappings
109     { "CONTROL", GDK_CONTROL_MASK }, // control
110     { "MOD1",    GDK_MOD1_MASK    }, // 4th mod - normally alt but depends on modmappings
111     { "MOD2",    GDK_MOD2_MASK    }, // 5th mod
112     { "MOD3",    GDK_MOD3_MASK    }, // 6th mod
113     { "MOD4",    GDK_MOD4_MASK    }, // 7th mod
114     { "MOD5",    GDK_MOD5_MASK    }, // 8th mod
115     { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
116     { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
117     { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
118     { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
119     { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
120     { "SUPER",   GDK_SUPER_MASK   }, // super (since 2.10)
121     { "HYPER",   GDK_HYPER_MASK   }, // hyper (since 2.10)
122     { "META",    GDK_META_MASK    }, // meta (since 2.10)
123     { NULL,      0                }
124 };
125
126 static void
127 dump_vars(gpointer key, gpointer val, gpointer data) {
128     (void) data;
129     uzbl_cmdprop *c;
130     int is_string;
131
132     c = val;
133     is_string = c->is_string;
134
135     if(is_string)
136         printf("KEY: %s\tVAL: %s\tIS_STRING: %s\n", 
137                 (char *)key, (char *)*c->ptr, "YES");
138     else
139         printf("KEY: %s\tVAL: %d\tIS_STRING: %s\n", 
140                 (char *)key, (int)*c->ptr, "NO");
141
142 }
143
144 /* construct a hash from the var_name_to_ptr array for quick access */
145 static void
146 make_var_to_name_hash() {
147     uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
148     while(n2v_p->name) {
149         g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
150         n2v_p++;
151     }
152 }
153
154 /* commandline arguments (set initial values for the state variables) */
155 static GOptionEntry entries[] =
156 {
157     { "uri",     'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,           "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
158     { "verbose", 'v', 0, G_OPTION_ARG_NONE,   &uzbl.state.verbose,       "Whether to print all messages or just errors.", NULL },
159     { "name",    'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name, "Name of the current instance (defaults to Xorg window id)", "NAME" },
160     { "config",  'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,   "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
161     { NULL,      0, 0, 0, NULL, NULL, NULL }
162 };
163
164 typedef void (*Command)(WebKitWebView*, const char *);
165
166 /* --- UTILITY FUNCTIONS --- */
167
168 char *
169 itos(int val) {
170     char tmp[20];
171
172     snprintf(tmp, sizeof(tmp), "%i", val);
173     return g_strdup(tmp);
174 }
175
176 static char *
177 str_replace (const char* search, const char* replace, const char* string) {
178     gchar **buf;
179     char *ret;
180
181     buf = g_strsplit (string, search, -1);
182     ret = g_strjoinv (replace, buf);
183     g_strfreev(buf);
184
185     return ret;
186 }
187
188 static sigfunc*
189 setup_signal(int signr, sigfunc *shandler) {
190     struct sigaction nh, oh;
191
192     nh.sa_handler = shandler;
193     sigemptyset(&nh.sa_mask);
194     nh.sa_flags = 0;
195
196     if(sigaction(signr, &nh, &oh) < 0)
197         return SIG_ERR;
198
199     return NULL;
200 }
201
202 static void
203 clean_up(void) {
204     if (uzbl.behave.fifo_dir)
205         unlink (uzbl.comm.fifo_path);
206     if (uzbl.behave.socket_dir)
207         unlink (uzbl.comm.socket_path);
208
209     g_free(uzbl.state.executable_path);
210     g_string_free(uzbl.state.keycmd, TRUE);
211     g_hash_table_destroy(uzbl.bindings);
212     g_hash_table_destroy(uzbl.behave.commands);
213 }
214
215
216 /* --- SIGNAL HANDLER --- */
217
218 static void
219 catch_sigterm(int s) {
220     (void) s;
221     clean_up();
222 }
223
224 static void
225 catch_sigint(int s) {
226     (void) s;
227     clean_up();
228     exit(EXIT_SUCCESS);
229 }
230
231 /* --- CALLBACKS --- */
232
233 static gboolean
234 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
235     (void) web_view;
236     (void) frame;
237     (void) navigation_action;
238     (void) policy_decision;
239     (void) user_data;
240     const gchar* uri = webkit_network_request_get_uri (request);
241     if (uzbl.state.verbose)
242         printf("New window requested -> %s \n", uri);
243     new_window_load_uri(uri);
244     return (FALSE);
245 }
246
247 WebKitWebView*
248 create_web_view_cb (WebKitWebView  *web_view, WebKitWebFrame *frame, gpointer user_data) {
249     (void) web_view;
250     (void) frame;
251     (void) user_data;
252     if (uzbl.state.selected_url != NULL) {
253         if (uzbl.state.verbose)
254             printf("\nNew web view -> %s\n",uzbl.state.selected_url);
255         new_window_load_uri(uzbl.state.selected_url);
256     } else {
257         if (uzbl.state.verbose)
258             printf("New web view -> %s\n","Nothing to open, exiting");
259     }
260     return (NULL);
261 }
262
263 static gboolean
264 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
265     (void) web_view;
266     (void) user_data;
267     if (uzbl.behave.download_handler) {
268         const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
269         if (uzbl.state.verbose)
270             printf("Download -> %s\n",uri);
271         /* if urls not escaped, we may have to escape and quote uri before this call */
272         run_handler(uzbl.behave.download_handler, uri);
273     }
274     return (FALSE);
275 }
276
277 /* scroll a bar in a given direction */
278 static void
279 scroll (GtkAdjustment* bar, const char *param) {
280     gdouble amount;
281     gchar *end;
282
283     amount = g_ascii_strtod(param, &end);
284     if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
285     gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
286 }
287
288 static void scroll_begin(WebKitWebView* page, const char *param) {
289     (void) page; (void) param;
290     gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
291 }
292
293 static void scroll_end(WebKitWebView* page, const char *param) {
294     (void) page; (void) param;
295     gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
296                               gtk_adjustment_get_page_size(uzbl.gui.bar_v));
297 }
298
299 static void scroll_vert(WebKitWebView* page, const char *param) {
300     (void) page;
301     scroll(uzbl.gui.bar_v, param);
302 }
303
304 static void scroll_horz(WebKitWebView* page, const char *param) {
305     (void) page;
306     scroll(uzbl.gui.bar_h, param);
307 }
308
309 static void
310 cmd_set_status() {
311     if (!uzbl.behave.show_status) {
312         gtk_widget_hide(uzbl.gui.mainbar);
313     } else {
314         gtk_widget_show(uzbl.gui.mainbar);
315     }
316     update_title();
317 }
318
319 static void
320 toggle_status_cb (WebKitWebView* page, const char *param) {
321     (void)page;
322     (void)param;
323
324     if (uzbl.behave.show_status) {
325         gtk_widget_hide(uzbl.gui.mainbar);
326     } else {
327         gtk_widget_show(uzbl.gui.mainbar);
328     }
329     uzbl.behave.show_status = !uzbl.behave.show_status;
330     update_title();
331 }
332
333 static void
334 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
335     (void) page;
336     (void) title;
337     (void) data;
338     //Set selected_url state variable
339     g_free(uzbl.state.selected_url);
340     uzbl.state.selected_url = NULL;
341     if (link) {
342         uzbl.state.selected_url = g_strdup(link);
343     }
344     update_title();
345 }
346
347 static void
348 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
349     (void) web_view;
350     (void) web_frame;
351     (void) data;
352     if (uzbl.gui.main_title)
353         g_free (uzbl.gui.main_title);
354     uzbl.gui.main_title = g_strdup (title);
355     update_title();
356 }
357
358 static void
359 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
360     (void) page;
361     (void) data;
362     uzbl.gui.sbar.load_progress = progress;
363     update_title();
364 }
365
366 static void
367 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
368     (void) page;
369     (void) frame;
370     (void) data;
371     if (uzbl.behave.load_finish_handler)
372         run_handler(uzbl.behave.load_finish_handler, "");
373 }
374
375 static void
376 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
377     (void) page;
378     (void) frame;
379     (void) data;
380     if (uzbl.behave.load_start_handler)
381         run_handler(uzbl.behave.load_start_handler, "");
382 }
383
384 static void
385 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
386     (void) page;
387     (void) data;
388     free (uzbl.state.uri);
389     GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
390     uzbl.state.uri = g_string_free (newuri, FALSE);
391     if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
392         uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
393         update_title();
394     }
395     g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
396     if (uzbl.behave.load_commit_handler)
397         run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
398 }
399
400 static void
401 destroy_cb (GtkWidget* widget, gpointer data) {
402     (void) widget;
403     (void) data;
404     gtk_main_quit ();
405 }
406
407 static void
408 log_history_cb () {
409    if (uzbl.behave.history_handler) {
410        time_t rawtime;
411        struct tm * timeinfo;
412        char date [80];
413        time ( &rawtime );
414        timeinfo = localtime ( &rawtime );
415        strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
416        run_handler(uzbl.behave.history_handler, date);
417    }
418 }
419
420
421 /* VIEW funcs (little webkit wrappers) */
422 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, const char *param){(void)param; webkit_web_view_##name(page);}
423 VIEWFUNC(reload)
424 VIEWFUNC(reload_bypass_cache)
425 VIEWFUNC(stop_loading)
426 VIEWFUNC(zoom_in)
427 VIEWFUNC(zoom_out)
428 VIEWFUNC(go_back)
429 VIEWFUNC(go_forward)
430 #undef VIEWFUNC
431
432 /* -- command to callback/function map for things we cannot attach to any signals */
433 // TODO: reload
434
435 static struct {char *name; Command command;} cmdlist[] =
436 {
437     { "back",             view_go_back            },
438     { "forward",          view_go_forward         },
439     { "scroll_vert",      scroll_vert             },
440     { "scroll_horz",      scroll_horz             },
441     { "scroll_begin",     scroll_begin            },
442     { "scroll_end",       scroll_end              },
443     { "reload",           view_reload,            },
444     { "reload_ign_cache", view_reload_bypass_cache},
445     { "stop",             view_stop_loading,      },
446     { "zoom_in",          view_zoom_in,           }, //Can crash (when max zoom reached?).
447     { "zoom_out",         view_zoom_out,          },
448     { "uri",              load_uri                },
449     { "script",           run_js                  },
450     { "toggle_status",    toggle_status_cb        },
451     { "spawn",            spawn                   },
452     { "sh",               spawn_sh                },
453     { "exit",             close_uzbl              },
454     { "search",           search_forward_text     },
455     { "search_reverse",   search_reverse_text     },
456     { "insert_mode",      set_insert_mode         },
457     { "runcmd",           runcmd                  }
458 };
459
460 static void
461 commands_hash(void)
462 {
463     unsigned int i;
464     uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
465
466     for (i = 0; i < LENGTH(cmdlist); i++)
467         g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
468 }
469
470 /* -- CORE FUNCTIONS -- */
471
472 void
473 free_action(gpointer act) {
474     Action *action = (Action*)act;
475     g_free(action->name);
476     if (action->param)
477         g_free(action->param);
478     g_free(action);
479 }
480
481 Action*
482 new_action(const gchar *name, const gchar *param) {
483     Action *action = g_new(Action, 1);
484
485     action->name = g_strdup(name);
486     if (param)
487         action->param = g_strdup(param);
488     else
489         action->param = NULL;
490
491     return action;
492 }
493
494 static bool
495 file_exists (const char * filename) {
496     return (access(filename, F_OK) == 0);
497 }
498
499 static void
500 set_insert_mode(WebKitWebView *page, const gchar *param) {
501     (void)page;
502     (void)param;
503
504     uzbl.behave.insert_mode = TRUE;
505     update_title();
506 }
507
508 static void
509 load_uri (WebKitWebView * web_view, const gchar *param) {
510     if (param) {
511         GString* newuri = g_string_new (param);
512         if (g_strrstr (param, "://") == NULL)
513             g_string_prepend (newuri, "http://");
514         /* if we do handle cookies, ask our handler for them */
515         webkit_web_view_load_uri (web_view, newuri->str);
516         g_string_free (newuri, TRUE);
517     }
518 }
519
520 static void
521 run_js (WebKitWebView * web_view, const gchar *param) {
522     if (param)
523         webkit_web_view_execute_script (web_view, param);
524 }
525
526 static void
527 search_text (WebKitWebView *page, const char *param, const gboolean forward) {
528     if ((param) && (param[0] != '\0')) {
529         uzbl.state.searchtx = g_strdup(param);
530     }
531     if (uzbl.state.searchtx != NULL) {
532         if (uzbl.state.verbose)
533             printf ("Searching: %s\n", uzbl.state.searchtx);
534         webkit_web_view_unmark_text_matches (page);
535         webkit_web_view_mark_text_matches (page, uzbl.state.searchtx, FALSE, 0);
536         webkit_web_view_set_highlight_text_matches (page, TRUE);
537         webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
538         g_free(uzbl.state.searchtx);
539         uzbl.state.searchtx = NULL;
540     }
541 }
542
543 static void
544 search_forward_text (WebKitWebView *page, const char *param) {
545     search_text(page, param, TRUE);
546 }
547
548 static void
549 search_reverse_text (WebKitWebView *page, const char *param) {
550     search_text(page, param, FALSE);
551 }
552
553 static void
554 new_window_load_uri (const gchar * uri) {
555     GString* to_execute = g_string_new ("");
556     g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
557     int i;
558     for (i = 0; entries[i].long_name != NULL; i++) {
559         if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
560             gchar** str = (gchar**)entries[i].arg_data;
561             if (*str!=NULL) {
562                 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
563             }
564         }
565     }
566     if (uzbl.state.verbose)
567         printf("\n%s\n", to_execute->str);
568     g_spawn_command_line_async (to_execute->str, NULL);
569     g_string_free (to_execute, TRUE);
570 }
571
572 static void
573 close_uzbl (WebKitWebView *page, const char *param) {
574     (void)page;
575     (void)param;
576     gtk_main_quit ();
577 }
578
579 /* --Statusbar functions-- */
580 static char*
581 build_progressbar_ascii(int percent) {
582    int width=10;
583    int i;
584    double l;
585    GString *bar = g_string_new("");
586
587    l = (double)percent*((double)width/100.);
588    l = (int)(l+.5)>=(int)l ? l+.5 : l;
589
590    for(i=0; i<(int)l; i++)
591        g_string_append(bar, "=");
592
593    for(; i<width; i++)
594        g_string_append(bar, "·");
595
596    return g_string_free(bar, FALSE);
597 }
598
599 static void
600 setup_scanner() {
601      const GScannerConfig scan_config = {
602              (
603               "\t\r\n"
604              )            /* cset_skip_characters */,
605              (
606               G_CSET_a_2_z
607               "_#"
608               G_CSET_A_2_Z
609              )            /* cset_identifier_first */,
610              (
611               G_CSET_a_2_z
612               "_0123456789"
613               G_CSET_A_2_Z
614               G_CSET_LATINS
615               G_CSET_LATINC
616              )            /* cset_identifier_nth */,
617              ( "" )    /* cpair_comment_single */,
618
619              TRUE         /* case_sensitive */,
620
621              FALSE        /* skip_comment_multi */,
622              FALSE        /* skip_comment_single */,
623              FALSE        /* scan_comment_multi */,
624              TRUE         /* scan_identifier */,
625              TRUE         /* scan_identifier_1char */,
626              FALSE        /* scan_identifier_NULL */,
627              TRUE         /* scan_symbols */,
628              FALSE        /* scan_binary */,
629              FALSE        /* scan_octal */,
630              FALSE        /* scan_float */,
631              FALSE        /* scan_hex */,
632              FALSE        /* scan_hex_dollar */,
633              FALSE        /* scan_string_sq */,
634              FALSE        /* scan_string_dq */,
635              TRUE         /* numbers_2_int */,
636              FALSE        /* int_2_float */,
637              FALSE        /* identifier_2_string */,
638              FALSE        /* char_2_token */,
639              FALSE        /* symbol_2_token */,
640              TRUE         /* scope_0_fallback */,
641              FALSE,
642              TRUE
643      };
644
645      uzbl.scan = g_scanner_new(&scan_config);
646      while(symp->symbol_name) {
647          g_scanner_scope_add_symbol(uzbl.scan, 0,
648                          symp->symbol_name,
649                          GINT_TO_POINTER(symp->symbol_token));
650          symp++;
651      }
652 }
653
654 static gchar *
655 expand_template(const char *template) {
656      if(!template) return NULL;
657
658      GTokenType token = G_TOKEN_NONE;
659      GString *ret = g_string_new("");
660      char *buf=NULL;
661      int sym;
662
663      g_scanner_input_text(uzbl.scan, template, strlen(template));
664      while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
665          token = g_scanner_get_next_token(uzbl.scan);
666
667          if(token == G_TOKEN_SYMBOL) {
668              sym = (int)g_scanner_cur_value(uzbl.scan).v_symbol;
669              switch(sym) {
670                  case SYM_URI:
671                      buf = uzbl.state.uri?
672                          g_markup_printf_escaped("%s", uzbl.state.uri) :
673                          g_strdup("");
674                      g_string_append(ret, buf);
675                      free(buf);
676                      break;
677                  case SYM_LOADPRGS:
678                      buf = itos(uzbl.gui.sbar.load_progress);
679                      g_string_append(ret, buf);
680                      free(buf);
681                      break;
682                  case SYM_LOADPRGSBAR:
683                      buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
684                      g_string_append(ret, buf);
685                      g_free(buf);
686                      break;
687                  case SYM_TITLE:
688                      buf = uzbl.gui.main_title?
689                          g_markup_printf_escaped("%s", uzbl.gui.main_title) :
690                          g_strdup("");
691                      g_string_append(ret, buf);
692                      free(buf);
693                      break;
694                  case SYM_SELECTED_URI:
695                      buf = uzbl.state.selected_url?
696                          g_markup_printf_escaped("%s", uzbl.state.selected_url) :
697                          g_strdup("");
698                      g_string_append(ret, buf);
699                      free(buf);
700                     break;
701                  case SYM_NAME:
702                      buf = itos(uzbl.xwin);
703                      g_string_append(ret,
704                          uzbl.state.instance_name?uzbl.state.instance_name:buf);
705                      free(buf);
706                      break;
707                  case SYM_KEYCMD:
708                      buf = uzbl.state.keycmd->str?
709                          g_markup_printf_escaped("%s", uzbl.state.keycmd->str) :
710                          g_strdup("");
711                      g_string_append(ret, buf);
712                      free(buf);
713                      break;
714                  case SYM_MODE:
715                      g_string_append(ret,
716                          uzbl.behave.insert_mode?"[I]":"[C]");
717                      break;
718                  case SYM_MSG:
719                      g_string_append(ret,
720                          uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
721                      break;
722                      /* useragent syms */
723                  case SYM_WK_MAJ:
724                      buf = itos(WEBKIT_MAJOR_VERSION);
725                      g_string_append(ret, buf);
726                      free(buf);
727                      break;
728                  case SYM_WK_MIN:
729                      buf = itos(WEBKIT_MINOR_VERSION);
730                      g_string_append(ret, buf);
731                      free(buf);
732                      break;
733                  case SYM_WK_MIC:
734                      buf = itos(WEBKIT_MICRO_VERSION);
735                      g_string_append(ret, buf);
736                      free(buf);
737                      break;
738                  case SYM_SYSNAME:
739                      g_string_append(ret, uzbl.state.unameinfo.sysname);
740                      break;
741                  case SYM_NODENAME:
742                      g_string_append(ret, uzbl.state.unameinfo.nodename);
743                      break;
744                  case SYM_KERNREL:
745                      g_string_append(ret, uzbl.state.unameinfo.release);
746                      break;
747                  case SYM_KERNVER:
748                      g_string_append(ret, uzbl.state.unameinfo.version);
749                      break;
750                  case SYM_ARCHSYS:
751                      g_string_append(ret, uzbl.state.unameinfo.machine);
752                      break;
753                  case SYM_ARCHUZBL:
754                      g_string_append(ret, ARCH);
755                      break;
756 #ifdef _GNU_SOURCE
757                  case SYM_DOMAINNAME:
758                      g_string_append(ret, uzbl.state.unameinfo.domainname);
759                      break;
760 #endif
761                  case SYM_COMMIT:
762                      g_string_append(ret, COMMIT);
763                      break;
764                  default:
765                      break;
766              }
767          }
768          else if(token == G_TOKEN_INT) {
769              buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
770              g_string_append(ret, buf);
771              free(buf);
772          }
773          else if(token == G_TOKEN_IDENTIFIER) {
774              g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
775          }
776          else if(token == G_TOKEN_CHAR) {
777              g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
778          }
779      }
780
781      return g_string_free(ret, FALSE);
782 }
783 /* --End Statusbar functions-- */
784
785 static void
786 sharg_append(GArray *a, const gchar *str) {
787     const gchar *s = (str ? str : "");
788     g_array_append_val(a, s);
789 }
790
791 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
792 static gboolean
793 run_command (const gchar *command, const guint npre, const gchar **args,
794              const gboolean sync, char **stdout) {
795    //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
796     GError *err = NULL;
797     
798     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
799     gchar *pid = itos(getpid());
800     gchar *xwin = itos(uzbl.xwin);
801     guint i;
802     sharg_append(a, command);
803     for (i = 0; i < npre; i++) /* add n args before the default vars */
804         sharg_append(a, args[i]);
805     sharg_append(a, uzbl.state.config_file);
806     sharg_append(a, pid);
807     sharg_append(a, xwin);
808     sharg_append(a, uzbl.comm.fifo_path);
809     sharg_append(a, uzbl.comm.socket_path);
810     sharg_append(a, uzbl.state.uri);
811     sharg_append(a, uzbl.gui.main_title);
812
813     for (i = npre; i < g_strv_length((gchar**)args); i++)
814         sharg_append(a, args[i]);
815     gboolean result;
816     if (sync) result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
817                                     NULL, NULL, stdout, NULL, NULL, &err);
818     else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
819                                 NULL, NULL, NULL, &err);
820
821     if (uzbl.state.verbose) {
822         GString *s = g_string_new("spawned:");
823         for (i = 0; i < (a->len); i++) {
824             gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
825             g_string_append_printf(s, " %s", qarg);
826             g_free (qarg);
827         }
828         g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
829         printf("%s\n", s->str);
830         g_string_free(s, TRUE);
831     }
832     if (err) {
833         g_printerr("error on run_command: %s\n", err->message);
834         g_error_free (err);
835     }
836     g_free (pid);
837     g_free (xwin);
838     g_array_free (a, TRUE);
839     return result;
840 }
841
842 static gchar**
843 split_quoted(const gchar* src, const gboolean unquote) {
844     /* split on unquoted space, return array of strings;
845        remove a layer of quotes and backslashes if unquote */
846     gboolean dq = FALSE;
847     gboolean sq = FALSE;
848     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
849     GString *s = g_string_new ("");
850     const gchar *p;
851     gchar **ret;
852     gchar *dup;
853     for (p = src; *p != '\0'; p++) {
854         if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
855         else if (*p == '\\') { g_string_append_c(s, *p++);
856                                g_string_append_c(s, *p); }
857         else if ((*p == '"') && unquote && !sq) dq = !dq;
858         else if (*p == '"' && !sq) { g_string_append_c(s, *p);
859                                      dq = !dq; }
860         else if ((*p == '\'') && unquote && !dq) sq = !sq;
861         else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
862                                       sq = ! sq; }
863         else if ((*p == ' ') && !dq && !sq) {
864             dup = g_strdup(s->str);
865             g_array_append_val(a, dup);
866             g_string_truncate(s, 0);
867         } else g_string_append_c(s, *p);
868     }
869     dup = g_strdup(s->str);
870     g_array_append_val(a, dup);
871     ret = (gchar**)a->data;
872     g_array_free (a, FALSE);
873     g_string_free (s, FALSE);
874     return ret;
875 }
876
877 static void
878 spawn(WebKitWebView *web_view, const char *param) {
879     (void)web_view;
880     //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
881     gchar **cmd = split_quoted(param, TRUE);
882     if (cmd) run_command(cmd[0], 0, &cmd[1], FALSE, NULL);
883     g_strfreev ((gchar**)cmd);
884 }
885
886 static void
887 spawn_sh(WebKitWebView *web_view, const char *param) {
888     (void)web_view;
889     if (!uzbl.behave.shell_cmd) {
890         g_printerr ("spawn_sh: shell_cmd is not set!\n");
891         return;
892     }
893     
894     guint i;
895     gchar *spacer = g_strdup("");
896     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
897     gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
898     gchar **p = split_quoted(param, TRUE);
899     for (i = 1; i < g_strv_length(cmd); i++)
900         sharg_append(a, cmd[i]);
901     sharg_append(a, p[0]); /* the first param comes right after shell_cmd;
902                               the rest come after default args */
903     sharg_append(a, spacer);
904     for (i = 1; i < g_strv_length(p); i++)
905         sharg_append(a, p[i]);
906     if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, a->data, FALSE, NULL);
907     g_free (spacer);
908     g_strfreev (cmd);
909     g_strfreev (p);
910     g_array_free (a, FALSE);
911 }
912
913 static void
914 parse_command(const char *cmd, const char *param) {
915     Command c;
916
917     if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd)))
918         c(uzbl.gui.web_view, param);
919     else
920         fprintf (stderr, "command \"%s\" not understood. ignoring.\n", cmd);
921 }
922
923 /* command parser */
924 static void
925 setup_regex() {
926     uzbl.comm.get_regex  = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
927             G_REGEX_OPTIMIZE, 0, NULL);
928     uzbl.comm.set_regex  = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
929             G_REGEX_OPTIMIZE, 0, NULL);
930     uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
931             G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, NULL);
932     uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
933             G_REGEX_OPTIMIZE, 0, NULL);
934     uzbl.comm.keycmd_regex = g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
935             G_REGEX_OPTIMIZE, 0, NULL);
936 }
937
938 static gboolean
939 get_var_value(gchar *name) {
940     uzbl_cmdprop *c;
941
942     if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
943         if(c->is_string)
944             printf("VAR: %s VALUE: %s\n", name, (char *)*c->ptr);
945         else 
946             printf("VAR: %s VALUE: %d\n", name, (int)*c->ptr);
947     }
948     return TRUE;
949 }
950
951 static void
952 set_proxy_url() {
953     SoupURI *suri;
954
955     if(*uzbl.net.proxy_url == ' '
956        || uzbl.net.proxy_url == NULL) {
957         soup_session_remove_feature_by_type(uzbl.net.soup_session,
958                 (GType) SOUP_SESSION_PROXY_URI);
959     }
960     else {
961         suri = soup_uri_new(uzbl.net.proxy_url);
962         g_object_set(G_OBJECT(uzbl.net.soup_session),
963                 SOUP_SESSION_PROXY_URI,
964                 suri, NULL);
965         soup_uri_free(suri);
966     }
967     return;
968 }
969
970 static void
971 cmd_load_uri() {
972     load_uri(uzbl.gui.web_view, uzbl.state.uri);
973 }
974
975 static void
976 move_statusbar() {
977     gtk_widget_ref(uzbl.gui.scrolled_win);
978     gtk_widget_ref(uzbl.gui.mainbar);
979     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
980     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
981
982     if(uzbl.behave.status_top) {
983         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
984         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
985     }
986     else {
987         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
988         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
989     }
990     gtk_widget_unref(uzbl.gui.scrolled_win);
991     gtk_widget_unref(uzbl.gui.mainbar);
992     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
993     return;
994 }
995
996 static gboolean
997 var_is(const char *x, const char *y) {
998     return (strcmp(x, y) == 0 ? TRUE : FALSE );
999 }
1000
1001 static gboolean
1002 set_var_value(gchar *name, gchar *val) {
1003     void *p = NULL;
1004     uzbl_cmdprop *c = NULL;
1005     char *endp = NULL;
1006     char *buf=NULL;
1007
1008     if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1009         /* check for the variable type */
1010         if (c->is_string) {
1011             free(*c->ptr);
1012             *c->ptr = g_strdup(val);
1013         } else {
1014             *c->ptr = (int)strtoul(val, &endp, 10);
1015         }
1016         
1017         /* invoke a command specific function */
1018         if(c->func)
1019             c->func();
1020
1021         /* this will be removed as soon as we have converted to
1022          * the callback interface
1023          */
1024         p = *c->ptr;
1025
1026         if(var_is("fifo_dir", name)) {
1027             if(p) free(p);
1028             buf = init_fifo(val);
1029             p = buf?buf:g_strdup("");
1030         }
1031         else if(var_is("socket_dir", name)) {
1032             if(p) free(p);
1033             buf = init_socket(val);
1034             p = buf?buf:g_strdup("");
1035         }
1036         else if(var_is("modkey", name)) {
1037             if(p) free(p);
1038             int i;
1039             p = g_utf8_strup(val, -1);
1040             uzbl.behave.modmask = 0;
1041             for (i = 0; modkeys[i].key != NULL; i++) {
1042                 if (g_strrstr(p, modkeys[i].key))
1043                     uzbl.behave.modmask |= modkeys[i].mask;
1044             }
1045         }
1046         else if(var_is("useragent", name)) {
1047             if(p) free(p);
1048             buf = set_useragent(val);
1049             p = buf?buf:g_strdup("");
1050         }
1051         else if(var_is("always_insert_mode", name)) {
1052             uzbl.behave.insert_mode =
1053                 uzbl.behave.always_insert_mode ?  TRUE : FALSE;
1054             update_title();
1055         }
1056         else if (var_is("max_conns", name)) {
1057             g_object_set(G_OBJECT(uzbl.net.soup_session),
1058                     SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1059         }
1060         else if (var_is("max_conns_host", name)) {
1061             g_object_set(G_OBJECT(uzbl.net.soup_session),
1062                     SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1063         }
1064         else if (var_is("http_debug", name)) {
1065             soup_session_remove_feature
1066                 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1067             /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1068             /*g_free(uzbl.net.soup_logger);*/
1069
1070             uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1071             soup_session_add_feature(uzbl.net.soup_session,
1072                     SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1073         }
1074         else if (var_is("default_font_size", name)) {
1075             WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1076             g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.default_font_size, NULL);
1077         }
1078         else if (var_is("minimum_font_size", name)) {
1079             WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1080             g_object_set (G_OBJECT(ws), "minimum-font-size", uzbl.behave.minimum_font_size, NULL);
1081         }
1082     }
1083     return TRUE;
1084 }
1085
1086 static void
1087 runcmd(WebKitWebView* page, const char *param) {
1088     (void) page;
1089     parse_cmd_line(param);
1090 }
1091
1092 static void
1093 parse_cmd_line(const char *ctl_line) {
1094     gchar **tokens;
1095
1096     /* SET command */
1097     if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1098         tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1099         if(tokens[0][0] == 0) {
1100             set_var_value(tokens[1], tokens[2]);
1101             g_strfreev(tokens);
1102         }
1103         else
1104             printf("Error in command: %s\n", tokens[0]);
1105     }
1106     /* GET command */
1107     else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1108         tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1109         if(tokens[0][0] == 0) {
1110             get_var_value(tokens[1]);
1111             g_strfreev(tokens);
1112         }
1113         else
1114             printf("Error in command: %s\n", tokens[0]);
1115     }
1116     /* BIND command */
1117     else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1118         tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1119         if(tokens[0][0] == 0) {
1120             add_binding(tokens[1], tokens[2]);
1121             g_strfreev(tokens);
1122         }
1123         else
1124             printf("Error in command: %s\n", tokens[0]);
1125     }
1126     /* ACT command */
1127     else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1128         tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1129         if(tokens[0][0] == 0) {
1130             parse_command(tokens[1], tokens[2]);
1131             g_strfreev(tokens);
1132         }
1133         else
1134             printf("Error in command: %s\n", tokens[0]);
1135     }
1136     /* KEYCMD command */
1137     else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1138         tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1139         if(tokens[0][0] == 0) {
1140             /* should incremental commands want each individual "keystroke"
1141                sent in a loop or the whole string in one go like now? */
1142             g_string_assign(uzbl.state.keycmd, tokens[1]);
1143             run_keycmd(FALSE);
1144             if (g_strstr_len(ctl_line, 7, "n") || g_strstr_len(ctl_line, 7, "N"))
1145                 run_keycmd(TRUE);
1146             update_title();
1147             g_strfreev(tokens);
1148         }
1149     }
1150     /* Comments */
1151     else if(   (ctl_line[0] == '#')
1152             || (ctl_line[0] == ' ')
1153             || (ctl_line[0] == '\n'))
1154         ; /* ignore these lines */
1155     else
1156         printf("Command not understood (%s)\n", ctl_line);
1157
1158     return;
1159 }
1160
1161 static gchar*
1162 build_stream_name(int type, const gchar* dir) {
1163     char *xwin_str;
1164     State *s = &uzbl.state;
1165     gchar *str;
1166
1167     xwin_str = itos((int)uzbl.xwin);
1168     if (type == FIFO) {
1169         str = g_strdup_printf
1170             ("%s/uzbl_fifo_%s", dir,
1171              s->instance_name ? s->instance_name : xwin_str);
1172     } else if (type == SOCKET) {
1173         str = g_strdup_printf
1174             ("%s/uzbl_socket_%s", dir,
1175              s->instance_name ? s->instance_name : xwin_str );
1176     }
1177     g_free(xwin_str);
1178     return str;
1179 }
1180
1181 static gboolean
1182 control_fifo(GIOChannel *gio, GIOCondition condition) {
1183     if (uzbl.state.verbose)
1184         printf("triggered\n");
1185     gchar *ctl_line;
1186     GIOStatus ret;
1187     GError *err = NULL;
1188
1189     if (condition & G_IO_HUP)
1190         g_error ("Fifo: Read end of pipe died!\n");
1191
1192     if(!gio)
1193        g_error ("Fifo: GIOChannel broke\n");
1194
1195     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1196     if (ret == G_IO_STATUS_ERROR) {
1197         g_error ("Fifo: Error reading: %s\n", err->message);
1198         g_error_free (err);
1199     }
1200
1201     parse_cmd_line(ctl_line);
1202     g_free(ctl_line);
1203
1204     return TRUE;
1205 }
1206
1207 static gchar*
1208 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1209     if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1210         if (unlink(uzbl.comm.fifo_path) == -1)
1211             g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1212         g_free(uzbl.comm.fifo_path);
1213         uzbl.comm.fifo_path = NULL;
1214     }
1215
1216     if (*dir == ' ') { /* space unsets the variable */
1217         return NULL;
1218     }
1219
1220     GIOChannel *chan = NULL;
1221     GError *error = NULL;
1222     gchar *path = build_stream_name(FIFO, dir);
1223
1224     if (!file_exists(path)) {
1225         if (mkfifo (path, 0666) == 0) {
1226             // 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.
1227             chan = g_io_channel_new_file(path, "r+", &error);
1228             if (chan) {
1229                 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1230                     if (uzbl.state.verbose)
1231                         printf ("init_fifo: created successfully as %s\n", path);
1232                     uzbl.comm.fifo_path = path;
1233                     return dir;
1234                 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1235             } else g_warning ("init_fifo: can't open: %s\n", error->message);
1236         } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1237     } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1238
1239     /* if we got this far, there was an error; cleanup */
1240     if (error) g_error_free (error);
1241     g_free(path);
1242     return NULL;
1243 }
1244
1245 static gboolean
1246 control_stdin(GIOChannel *gio, GIOCondition condition) {
1247     (void) condition;
1248     gchar *ctl_line = NULL;
1249     GIOStatus ret;
1250
1251     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1252     if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1253         return FALSE;
1254
1255     parse_cmd_line(ctl_line);
1256     g_free(ctl_line);
1257
1258     return TRUE;
1259 }
1260
1261 static void
1262 create_stdin () {
1263     GIOChannel *chan = NULL;
1264     GError *error = NULL;
1265
1266     chan = g_io_channel_unix_new(fileno(stdin));
1267     if (chan) {
1268         if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1269             g_error ("Stdin: could not add watch\n");
1270         } else {
1271             if (uzbl.state.verbose)
1272                 printf ("Stdin: watch added successfully\n");
1273         }
1274     } else {
1275         g_error ("Stdin: Error while opening: %s\n", error->message);
1276     }
1277     if (error) g_error_free (error);
1278 }
1279
1280 static gboolean
1281 control_socket(GIOChannel *chan) {
1282     struct sockaddr_un remote;
1283     char buffer[512], *ctl_line;
1284     char temp[128];
1285     int sock, clientsock, n, done;
1286     unsigned int t;
1287
1288     sock = g_io_channel_unix_get_fd(chan);
1289
1290     memset (buffer, 0, sizeof (buffer));
1291
1292     t          = sizeof (remote);
1293     clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1294
1295     done = 0;
1296     do {
1297         memset (temp, 0, sizeof (temp));
1298         n = recv (clientsock, temp, 128, 0);
1299         if (n == 0) {
1300             buffer[strlen (buffer)] = '\0';
1301             done = 1;
1302         }
1303         if (!done)
1304             strcat (buffer, temp);
1305     } while (!done);
1306
1307     if (strcmp (buffer, "\n") < 0) {
1308         buffer[strlen (buffer) - 1] = '\0';
1309     } else {
1310         buffer[strlen (buffer)] = '\0';
1311     }
1312     close (clientsock);
1313     ctl_line = g_strdup(buffer);
1314     parse_cmd_line (ctl_line);
1315
1316 /*
1317    TODO: we should be able to do it with this.  but glib errors out with "Invalid argument"
1318     GError *error = NULL;
1319     gsize len;
1320     GIOStatus ret;
1321     ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1322     if (ret == G_IO_STATUS_ERROR)
1323         g_error ("Error reading: %s\n", error->message);
1324
1325     printf("Got line %s (%u bytes) \n",ctl_line, len);
1326     if(ctl_line) {
1327        parse_line(ctl_line);
1328 */
1329
1330     g_free(ctl_line);
1331     return TRUE;
1332 }
1333
1334 static gchar*
1335 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1336     if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1337         if (unlink(uzbl.comm.socket_path) == -1)
1338             g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1339         g_free(uzbl.comm.socket_path);
1340         uzbl.comm.socket_path = NULL;
1341     }
1342
1343     if (*dir == ' ') {
1344         g_free(dir);
1345         return NULL;
1346     }
1347
1348     GIOChannel *chan = NULL;
1349     int sock, len;
1350     struct sockaddr_un local;
1351     gchar *path = build_stream_name(SOCKET, dir);
1352
1353     sock = socket (AF_UNIX, SOCK_STREAM, 0);
1354
1355     local.sun_family = AF_UNIX;
1356     strcpy (local.sun_path, path);
1357     unlink (local.sun_path);
1358
1359     len = strlen (local.sun_path) + sizeof (local.sun_family);
1360     if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1361         if (uzbl.state.verbose)
1362             printf ("init_socket: opened in %s\n", path);
1363         listen (sock, 5);
1364
1365         if( (chan = g_io_channel_unix_new(sock)) ) {
1366             g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1367             uzbl.comm.socket_path = path;
1368             return dir;
1369         }
1370     } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1371
1372     /* if we got this far, there was an error; cleanup */
1373     g_free(path);
1374     g_free(dir);
1375     return NULL;
1376 }
1377
1378 /*
1379  NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1380  it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1381 */
1382 // this function may be called very early when the templates are not set (yet), hence the checks
1383 static void
1384 update_title (void) {
1385     Behaviour *b = &uzbl.behave;
1386     gchar *parsed;
1387
1388     if (b->show_status) {
1389         if (b->title_format_short) {
1390             parsed = expand_template(b->title_format_short);
1391             gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1392             g_free(parsed);
1393         }
1394         if (b->status_format) {
1395             parsed = expand_template(b->status_format);
1396             gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1397             g_free(parsed);
1398         }
1399         if (b->status_background) {
1400             GdkColor color;
1401             gdk_color_parse (b->status_background, &color);
1402             //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)
1403             gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1404         }
1405     } else {
1406         if (b->title_format_long) {
1407             parsed = expand_template(b->title_format_long);
1408             gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1409             g_free(parsed);
1410         }
1411     }
1412 }
1413
1414 static gboolean
1415 key_press_cb (WebKitWebView* page, GdkEventKey* event)
1416 {
1417     //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1418
1419     (void) page;
1420
1421     if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1422         || 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)
1423         return FALSE;
1424
1425     /* turn off insert mode (if always_insert_mode is not used) */
1426     if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1427         uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1428         update_title();
1429         return TRUE;
1430     }
1431
1432     if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1433         return FALSE;
1434
1435     if (event->keyval == GDK_Escape) {
1436         g_string_truncate(uzbl.state.keycmd, 0);
1437         update_title();
1438         return TRUE;
1439     }
1440
1441     //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1442     if (event->keyval == GDK_Insert) {
1443         gchar * str;
1444         if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1445             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1446         } else {
1447             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1448         }
1449         if (str) {
1450             g_string_append (uzbl.state.keycmd, str);
1451             update_title ();
1452             free (str);
1453         }
1454         return TRUE;
1455     }
1456
1457     if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1458         g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1459         update_title();
1460     }
1461
1462     gboolean key_ret = FALSE;
1463     if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1464         key_ret = TRUE;
1465     if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1466
1467     run_keycmd(key_ret);
1468     update_title();
1469     if (key_ret) return (!uzbl.behave.insert_mode);
1470     return TRUE;
1471 }
1472
1473 static void
1474 run_keycmd(const gboolean key_ret) {
1475     /* run the keycmd immediately if it isn't incremental and doesn't take args */
1476     Action *action;
1477     if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1478         g_string_truncate(uzbl.state.keycmd, 0);
1479         parse_command(action->name, action->param);
1480         return;
1481     }
1482
1483     /* try if it's an incremental keycmd or one that takes args, and run it */
1484     GString* short_keys = g_string_new ("");
1485     GString* short_keys_inc = g_string_new ("");
1486     unsigned int i;
1487     for (i=0; i<(uzbl.state.keycmd->len); i++) {
1488         g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1489         g_string_assign(short_keys_inc, short_keys->str);
1490         g_string_append_c(short_keys, '_');
1491         g_string_append_c(short_keys_inc, '*');
1492
1493         gboolean exec_now = FALSE;
1494         if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1495             if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1496         } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1497             if (key_ret) { /* just quit the incremental command on return */
1498                 g_string_truncate(uzbl.state.keycmd, 0);
1499                 break;
1500             } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1501         }
1502
1503         if (exec_now) {
1504             GString* parampart = g_string_new (uzbl.state.keycmd->str);
1505             GString* actionname = g_string_new ("");
1506             GString* actionparam = g_string_new ("");
1507             g_string_erase (parampart, 0, i+1);
1508             if (action->name)
1509                 g_string_printf (actionname, action->name, parampart->str);
1510             if (action->param)
1511                 g_string_printf (actionparam, action->param, parampart->str);
1512             parse_command(actionname->str, actionparam->str);
1513             g_string_free (actionname, TRUE);
1514             g_string_free (actionparam, TRUE);
1515             g_string_free (parampart, TRUE);
1516             if (key_ret)
1517                 g_string_truncate(uzbl.state.keycmd, 0);
1518             break;
1519         }
1520
1521         g_string_truncate(short_keys, short_keys->len - 1);
1522     }
1523     g_string_free (short_keys, TRUE);
1524     g_string_free (short_keys_inc, TRUE);
1525 }
1526
1527 static GtkWidget*
1528 create_browser () {
1529     GUI *g = &uzbl.gui;
1530
1531     GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1532     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
1533
1534     g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1535     gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1536
1537     g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1538     g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1539     g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1540     g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
1541     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1542     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1543     g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1544     g_signal_connect (G_OBJECT (g->web_view), "key-press-event", G_CALLBACK (key_press_cb), g->web_view);
1545     g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1546     g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1547     g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1548
1549     return scrolled_window;
1550 }
1551
1552 static GtkWidget*
1553 create_mainbar () {
1554     GUI *g = &uzbl.gui;
1555
1556     g->mainbar = gtk_hbox_new (FALSE, 0);
1557
1558     g->mainbar_label = gtk_label_new ("");
1559     gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1560     gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1561     gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1562     gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1563     gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1564     return g->mainbar;
1565 }
1566
1567 static
1568 GtkWidget* create_window () {
1569     GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1570     gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1571     gtk_widget_set_name (window, "Uzbl browser");
1572     g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1573
1574     return window;
1575 }
1576
1577 static void
1578 run_handler (const gchar *act, const gchar *args) {
1579     char **parts = g_strsplit(act, " ", 2);
1580     if (!parts) return;
1581     else if ((g_strcmp0(parts[0], "spawn") == 0)
1582              || (g_strcmp0(parts[0], "sh") == 0)) {
1583         guint i;
1584         GString *a = g_string_new ("");
1585         char **spawnparts;
1586         spawnparts = split_quoted(parts[1], FALSE);
1587         g_string_append_printf(a, "%s", spawnparts[0]);
1588         if (args) g_string_append_printf(a, " %s", args); /* append handler args before user args */
1589         for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
1590             g_string_append_printf(a, " %s", spawnparts[i]);
1591         parse_command(parts[0], a->str);
1592         g_string_free (a, TRUE);
1593         g_strfreev (spawnparts);
1594     } else
1595         parse_command(parts[0], parts[1]);
1596     g_strfreev (parts);
1597 }
1598
1599 static void
1600 add_binding (const gchar *key, const gchar *act) {
1601     char **parts = g_strsplit(act, " ", 2);
1602     Action *action;
1603
1604     if (!parts)
1605         return;
1606
1607     //Debug:
1608     if (uzbl.state.verbose)
1609         printf ("Binding %-10s : %s\n", key, act);
1610
1611     action = new_action(parts[0], parts[1]);
1612     g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
1613
1614     g_strfreev(parts);
1615 }
1616
1617 static gchar*
1618 get_xdg_var (XDG_Var xdg) {
1619     const gchar* actual_value = getenv (xdg.environmental);
1620     const gchar* home         = getenv ("HOME");
1621
1622     gchar* return_value = str_replace ("~", home, actual_value);
1623
1624     if (! actual_value || strcmp (actual_value, "") == 0) {
1625         if (xdg.default_value) {
1626             return_value = str_replace ("~", home, xdg.default_value);
1627         } else {
1628             return_value = NULL;
1629         }
1630     }
1631     return return_value;
1632 }
1633
1634 static gchar*
1635 find_xdg_file (int xdg_type, char* filename) {
1636     /* xdg_type = 0 => config
1637        xdg_type = 1 => data
1638        xdg_type = 2 => cache*/
1639
1640     gchar* temporary_file   = malloc (1024);
1641     gchar* temporary_string = NULL;
1642     char*  saveptr;
1643     char*  buf;
1644
1645     buf = get_xdg_var (XDG[xdg_type]);
1646     strcpy (temporary_file, buf);
1647     strcat (temporary_file, filename);
1648     free(buf);
1649
1650     if (! file_exists (temporary_file) && xdg_type != 2) {
1651         buf = get_xdg_var (XDG[3 + xdg_type]);
1652         temporary_string = (char *) strtok_r (buf, ":", &saveptr);
1653         free(buf);
1654
1655         while (temporary_string && ! file_exists (temporary_file)) {
1656             strcpy (temporary_file, temporary_string);
1657             strcat (temporary_file, filename);
1658             temporary_string = (char * ) strtok_r (NULL, ":", &saveptr);
1659         }
1660     }
1661
1662     if (file_exists (temporary_file)) {
1663         return temporary_file;
1664     } else {
1665         return NULL;
1666     }
1667 }
1668
1669 static void
1670 settings_init () {
1671     State *s = &uzbl.state;
1672     Network *n = &uzbl.net;
1673
1674     uzbl.behave.reset_command_mode = 1;
1675
1676     if (!s->config_file) {
1677         s->config_file = find_xdg_file (0, "/uzbl/config");
1678     }
1679
1680     if (s->config_file) {
1681         GIOChannel *chan = NULL;
1682         gchar *readbuf = NULL;
1683         gsize len;
1684
1685         chan = g_io_channel_new_file(s->config_file, "r", NULL);
1686
1687         if (chan) {
1688             while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL)
1689                     == G_IO_STATUS_NORMAL) {
1690                 parse_cmd_line(readbuf);
1691                 g_free (readbuf);
1692             }
1693
1694             g_io_channel_unref (chan);
1695             if (uzbl.state.verbose)
1696                 printf ("Config %s loaded\n", s->config_file);
1697         } else {
1698             fprintf(stderr, "uzbl: error loading file%s\n", s->config_file);
1699         }
1700     } else {
1701         if (uzbl.state.verbose)
1702             printf ("No configuration file loaded.\n");
1703     }
1704     if (!uzbl.behave.status_format)
1705         set_var_value("status_format", STATUS_DEFAULT);
1706     if (!uzbl.behave.title_format_long)
1707         set_var_value("title_format_long", TITLE_LONG_DEFAULT);
1708     if (!uzbl.behave.title_format_short)
1709         set_var_value("title_format_short", TITLE_SHORT_DEFAULT);
1710
1711
1712     g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1713 }
1714
1715 static gchar*
1716 set_useragent(gchar *val) {
1717     if (*val == ' ') {
1718         g_free(val);
1719         return NULL;
1720     }
1721     gchar *ua = expand_template(val);
1722     if (ua)
1723         g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1724     return ua;
1725 }
1726
1727 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1728     (void) session;
1729     (void) user_data;
1730     if (!uzbl.behave.cookie_handler) return;
1731
1732     gchar * stdout = NULL;
1733     soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1734     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1735     gchar *action = g_strdup ("GET");
1736     SoupURI * soup_uri = soup_message_get_uri(msg);
1737     sharg_append(a, action);
1738     sharg_append(a, soup_uri->host);
1739     sharg_append(a, soup_uri->path);
1740     run_command(uzbl.behave.cookie_handler, 0, a->data, TRUE, &stdout); /* TODO: use handler */
1741     //run_handler(uzbl.behave.cookie_handler); /* TODO: global stdout pointer, spawn_sync */
1742     if(stdout) {
1743         soup_message_headers_replace (msg->request_headers, "Cookie", stdout);
1744     }
1745     g_free (action);
1746     g_array_free(a, TRUE);
1747 }
1748
1749 static void
1750 save_cookies (SoupMessage *msg, gpointer user_data){
1751     (void) user_data;
1752     GSList *ck;
1753     char *cookie;
1754     for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1755         cookie = soup_cookie_to_set_cookie_header(ck->data);
1756         GArray *a = g_array_new(TRUE, FALSE, sizeof(gchar*));
1757         SoupURI * soup_uri = soup_message_get_uri(msg);
1758         gchar *action = strdup("PUT");
1759         sharg_append(a, action);
1760         sharg_append(a, soup_uri->host);
1761         sharg_append(a, soup_uri->path);
1762         sharg_append(a, cookie);
1763         run_command(uzbl.behave.cookie_handler, 0, a->data, FALSE, NULL);
1764         g_free (cookie);
1765         g_free (action);
1766         g_array_free(a, TRUE);
1767     }
1768     g_slist_free(ck);
1769 }
1770
1771
1772 int
1773 main (int argc, char* argv[]) {
1774     gtk_init (&argc, &argv);
1775     if (!g_thread_supported ())
1776         g_thread_init (NULL);
1777
1778     uzbl.state.executable_path = g_strdup(argv[0]);
1779     uzbl.state.selected_url = NULL;
1780     uzbl.state.searchtx = NULL;
1781
1782     GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
1783     g_option_context_add_main_entries (context, entries, NULL);
1784     g_option_context_add_group (context, gtk_get_option_group (TRUE));
1785     g_option_context_parse (context, &argc, &argv, NULL);
1786     g_option_context_free(context);
1787     /* initialize hash table */
1788     uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
1789
1790     uzbl.net.soup_session = webkit_get_default_session();
1791     uzbl.state.keycmd = g_string_new("");
1792
1793     if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
1794         fprintf(stderr, "uzbl: error hooking SIGTERM\n");
1795     if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
1796         fprintf(stderr, "uzbl: error hooking SIGINT\n");
1797
1798     if(uname(&uzbl.state.unameinfo) == -1)
1799         g_printerr("Can't retrieve unameinfo.  Your useragent might appear wrong.\n");
1800
1801     setup_regex();
1802     setup_scanner();
1803     commands_hash ();
1804     make_var_to_name_hash();
1805
1806
1807     uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
1808
1809     uzbl.gui.scrolled_win = create_browser();
1810     create_mainbar();
1811
1812     /* initial packing */
1813     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1814     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1815
1816     uzbl.gui.main_window = create_window ();
1817     gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
1818
1819
1820     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1821     gtk_widget_show_all (uzbl.gui.main_window);
1822     uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
1823
1824     if (uzbl.state.verbose) {
1825         printf("Uzbl start location: %s\n", argv[0]);
1826         printf("window_id %i\n",(int) uzbl.xwin);
1827         printf("pid %i\n", getpid ());
1828         printf("name: %s\n", uzbl.state.instance_name);
1829     }
1830
1831     uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
1832     uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
1833     uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
1834     uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
1835     gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
1836
1837     settings_init ();
1838
1839     if (!uzbl.behave.show_status)
1840         gtk_widget_hide(uzbl.gui.mainbar);
1841     else
1842         update_title();
1843
1844     create_stdin();
1845
1846     if(uzbl.state.uri)
1847         load_uri (uzbl.gui.web_view, uzbl.state.uri);
1848
1849     gtk_main ();
1850     clean_up();
1851
1852     return EXIT_SUCCESS;
1853 }
1854
1855 /* vi: set et ts=4: */