be more specific about what we assign to what in var_name_to_ptr
[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 } uzbl_cmdprop;
66
67 const struct {
68     char *name;
69     //void **ptr;
70     uzbl_cmdprop cp;
71 } var_name_to_ptr[] = {
72     { "uri",                {.ptr = (void *)&uzbl.state.uri,                   .is_string = 1}},
73     { "status_message",     {.ptr = (void *)&uzbl.gui.sbar.msg,                .is_string = 1}},
74     { "show_status",        {.ptr = (void *)&uzbl.behave.show_status,          .is_string = 0}},
75     { "status_top",         {.ptr = (void *)&uzbl.behave.status_top,           .is_string = 0}},
76     { "status_format",      {.ptr = (void *)&uzbl.behave.status_format,        .is_string = 1}},
77     { "status_background",  {.ptr = (void *)&uzbl.behave.status_background,    .is_string = 1}},
78     { "title_format_long",  {.ptr = (void *)&uzbl.behave.title_format_long,    .is_string = 1}},
79     { "title_format_short", {.ptr = (void *)&uzbl.behave.title_format_short,   .is_string = 1}},
80     { "insert_mode",        {.ptr = (void *)&uzbl.behave.insert_mode,          .is_string = 0}},
81     { "always_insert_mode", {.ptr = (void *)&uzbl.behave.always_insert_mode,   .is_string = 0}},
82     { "reset_command_mode", {.ptr = (void *)&uzbl.behave.reset_command_mode,   .is_string = 0}},
83     { "modkey"     ,        {.ptr = (void *)&uzbl.behave.modkey,               .is_string = 0}},
84     { "load_finish_handler",{.ptr = (void *)&uzbl.behave.load_finish_handler,  .is_string = 1}},
85     { "load_start_handler", {.ptr = (void *)&uzbl.behave.load_start_handler,   .is_string = 1}},
86     { "load_commit_handler",{.ptr = (void *)&uzbl.behave.load_commit_handler,  .is_string = 1}},
87     { "history_handler",    {.ptr = (void *)&uzbl.behave.history_handler,      .is_string = 1}},
88     { "download_handler",   {.ptr = (void *)&uzbl.behave.download_handler,     .is_string = 1}},
89     { "cookie_handler",     {.ptr = (void *)&uzbl.behave.cookie_handler,       .is_string = 1}},
90     { "fifo_dir",           {.ptr = (void *)&uzbl.behave.fifo_dir,             .is_string = 1}},
91     { "socket_dir",         {.ptr = (void *)&uzbl.behave.socket_dir,           .is_string = 1}},
92     { "http_debug",         {.ptr = (void *)&uzbl.behave.http_debug,           .is_string = 0}},
93     { "default_font_size",  {.ptr = (void *)&uzbl.behave.default_font_size,    .is_string = 0}},
94     { "minimum_font_size",  {.ptr = (void *)&uzbl.behave.minimum_font_size,    .is_string = 0}},
95     { "shell_cmd",          {.ptr = (void *)&uzbl.behave.shell_cmd,            .is_string = 1}},
96     { "proxy_url",          {.ptr = (void *)&uzbl.net.proxy_url,               .is_string = 1}},
97     { "max_conns",          {.ptr = (void *)&uzbl.net.max_conns,               .is_string = 1}},
98     { "max_conns_host",     {.ptr = (void *)&uzbl.net.max_conns_host,          .is_string = 0}},
99     { "useragent",          {.ptr = (void *)&uzbl.net.useragent,               .is_string = 1}},
100     { NULL,                 {.ptr = NULL,                                      .is_string = 0}}
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
971 static void
972 move_statusbar() {
973     gtk_widget_ref(uzbl.gui.scrolled_win);
974     gtk_widget_ref(uzbl.gui.mainbar);
975     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
976     gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
977
978     if(uzbl.behave.status_top) {
979         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
980         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
981     }
982     else {
983         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
984         gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
985     }
986     gtk_widget_unref(uzbl.gui.scrolled_win);
987     gtk_widget_unref(uzbl.gui.mainbar);
988     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
989 }
990
991 static gboolean
992 var_is(const char *x, const char *y) {
993     return (strcmp(x, y) == 0 ? TRUE : FALSE );
994 }
995
996 static gboolean
997 set_var_value(gchar *name, gchar *val) {
998     void *p = NULL;
999     uzbl_cmdprop *c = NULL;
1000     char *endp = NULL;
1001     char *buf=NULL;
1002
1003     if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1004         if (c->is_string) {
1005             free(*c->ptr);
1006             *c->ptr = g_strdup(val);
1007         } else
1008             *c->ptr = (int)strtoul(val, &endp, 10);
1009         p = *c->ptr;
1010
1011         if(var_is("status_message", name)
1012                 || var_is("status_background", name)
1013                 || var_is("status_format", name)
1014                 || var_is("title_format_long", name)
1015                 || var_is("title_format_short", name)
1016                 || var_is("load_finish_handler", name)
1017                 || var_is("load_start_handler", name)
1018                 || var_is("load_commit_handler", name)
1019                 || var_is("history_handler", name)
1020                 || var_is("download_handler", name)
1021                 || var_is("cookie_handler", name)) {
1022             update_title();
1023         }
1024         else if(var_is("uri", name)) {
1025             load_uri(uzbl.gui.web_view, uzbl.state.uri);
1026         }
1027         else if(var_is("proxy_url", name)) {
1028             set_proxy_url();
1029         }
1030         else if(var_is("fifo_dir", name)) {
1031             if(p) free(p);
1032             buf = init_fifo(val);
1033             p = buf?buf:g_strdup("");
1034         }
1035         else if(var_is("socket_dir", name)) {
1036             if(p) free(p);
1037             buf = init_socket(val);
1038             p = buf?buf:g_strdup("");
1039         }
1040         else if(var_is("modkey", name)) {
1041             if(p) free(p);
1042             int i;
1043             p = g_utf8_strup(val, -1);
1044             uzbl.behave.modmask = 0;
1045             for (i = 0; modkeys[i].key != NULL; i++) {
1046                 if (g_strrstr(p, modkeys[i].key))
1047                     uzbl.behave.modmask |= modkeys[i].mask;
1048             }
1049         }
1050         else if(var_is("useragent", name)) {
1051             if(p) free(p);
1052             buf = set_useragent(val);
1053             p = buf?buf:g_strdup("");
1054         }
1055         else if(var_is("show_status", name)) {
1056             cmd_set_status();
1057         }
1058         else if(var_is("always_insert_mode", name)) {
1059             uzbl.behave.insert_mode =
1060                 uzbl.behave.always_insert_mode ?  TRUE : FALSE;
1061             update_title();
1062         }
1063         else if (var_is("max_conns", name)) {
1064             g_object_set(G_OBJECT(uzbl.net.soup_session),
1065                     SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1066         }
1067         else if (var_is("max_conns_host", name)) {
1068             g_object_set(G_OBJECT(uzbl.net.soup_session),
1069                     SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1070         }
1071         else if (var_is("http_debug", name)) {
1072             soup_session_remove_feature
1073                 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1074             /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1075             /*g_free(uzbl.net.soup_logger);*/
1076
1077             uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1078             soup_session_add_feature(uzbl.net.soup_session,
1079                     SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1080         }
1081         else if (var_is("status_top", name)) {
1082             move_statusbar();
1083         }
1084         else if (var_is("default_font_size", name)) {
1085             WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1086             g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.default_font_size, NULL);
1087         }
1088         else if (var_is("minimum_font_size", name)) {
1089             WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1090             g_object_set (G_OBJECT(ws), "minimum-font-size", uzbl.behave.minimum_font_size, NULL);
1091         }
1092     }
1093     return TRUE;
1094 }
1095
1096 static void
1097 runcmd(WebKitWebView* page, const char *param) {
1098     (void) page;
1099     parse_cmd_line(param);
1100 }
1101
1102 static void
1103 parse_cmd_line(const char *ctl_line) {
1104     gchar **tokens;
1105
1106     /* SET command */
1107     if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1108         tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1109         if(tokens[0][0] == 0) {
1110             set_var_value(tokens[1], tokens[2]);
1111             g_strfreev(tokens);
1112         }
1113         else
1114             printf("Error in command: %s\n", tokens[0]);
1115     }
1116     /* GET command */
1117     else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1118         tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1119         if(tokens[0][0] == 0) {
1120             get_var_value(tokens[1]);
1121             g_strfreev(tokens);
1122         }
1123         else
1124             printf("Error in command: %s\n", tokens[0]);
1125     }
1126     /* BIND command */
1127     else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1128         tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1129         if(tokens[0][0] == 0) {
1130             add_binding(tokens[1], tokens[2]);
1131             g_strfreev(tokens);
1132         }
1133         else
1134             printf("Error in command: %s\n", tokens[0]);
1135     }
1136     /* ACT command */
1137     else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1138         tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1139         if(tokens[0][0] == 0) {
1140             parse_command(tokens[1], tokens[2]);
1141             g_strfreev(tokens);
1142         }
1143         else
1144             printf("Error in command: %s\n", tokens[0]);
1145     }
1146     /* KEYCMD command */
1147     else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1148         tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1149         if(tokens[0][0] == 0) {
1150             /* should incremental commands want each individual "keystroke"
1151                sent in a loop or the whole string in one go like now? */
1152             g_string_assign(uzbl.state.keycmd, tokens[1]);
1153             run_keycmd(FALSE);
1154             if (g_strstr_len(ctl_line, 7, "n") || g_strstr_len(ctl_line, 7, "N"))
1155                 run_keycmd(TRUE);
1156             update_title();
1157             g_strfreev(tokens);
1158         }
1159     }
1160     /* Comments */
1161     else if(   (ctl_line[0] == '#')
1162             || (ctl_line[0] == ' ')
1163             || (ctl_line[0] == '\n'))
1164         ; /* ignore these lines */
1165     else
1166         printf("Command not understood (%s)\n", ctl_line);
1167
1168     return;
1169 }
1170
1171 static gchar*
1172 build_stream_name(int type, const gchar* dir) {
1173     char *xwin_str;
1174     State *s = &uzbl.state;
1175     gchar *str;
1176
1177     xwin_str = itos((int)uzbl.xwin);
1178     if (type == FIFO) {
1179         str = g_strdup_printf
1180             ("%s/uzbl_fifo_%s", dir,
1181              s->instance_name ? s->instance_name : xwin_str);
1182     } else if (type == SOCKET) {
1183         str = g_strdup_printf
1184             ("%s/uzbl_socket_%s", dir,
1185              s->instance_name ? s->instance_name : xwin_str );
1186     }
1187     g_free(xwin_str);
1188     return str;
1189 }
1190
1191 static gboolean
1192 control_fifo(GIOChannel *gio, GIOCondition condition) {
1193     if (uzbl.state.verbose)
1194         printf("triggered\n");
1195     gchar *ctl_line;
1196     GIOStatus ret;
1197     GError *err = NULL;
1198
1199     if (condition & G_IO_HUP)
1200         g_error ("Fifo: Read end of pipe died!\n");
1201
1202     if(!gio)
1203        g_error ("Fifo: GIOChannel broke\n");
1204
1205     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1206     if (ret == G_IO_STATUS_ERROR) {
1207         g_error ("Fifo: Error reading: %s\n", err->message);
1208         g_error_free (err);
1209     }
1210
1211     parse_cmd_line(ctl_line);
1212     g_free(ctl_line);
1213
1214     return TRUE;
1215 }
1216
1217 static gchar*
1218 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1219     if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1220         if (unlink(uzbl.comm.fifo_path) == -1)
1221             g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1222         g_free(uzbl.comm.fifo_path);
1223         uzbl.comm.fifo_path = NULL;
1224     }
1225
1226     if (*dir == ' ') { /* space unsets the variable */
1227         return NULL;
1228     }
1229
1230     GIOChannel *chan = NULL;
1231     GError *error = NULL;
1232     gchar *path = build_stream_name(FIFO, dir);
1233
1234     if (!file_exists(path)) {
1235         if (mkfifo (path, 0666) == 0) {
1236             // 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.
1237             chan = g_io_channel_new_file(path, "r+", &error);
1238             if (chan) {
1239                 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1240                     if (uzbl.state.verbose)
1241                         printf ("init_fifo: created successfully as %s\n", path);
1242                     uzbl.comm.fifo_path = path;
1243                     return dir;
1244                 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1245             } else g_warning ("init_fifo: can't open: %s\n", error->message);
1246         } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1247     } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1248
1249     /* if we got this far, there was an error; cleanup */
1250     if (error) g_error_free (error);
1251     g_free(path);
1252     return NULL;
1253 }
1254
1255 static gboolean
1256 control_stdin(GIOChannel *gio, GIOCondition condition) {
1257     (void) condition;
1258     gchar *ctl_line = NULL;
1259     GIOStatus ret;
1260
1261     ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1262     if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1263         return FALSE;
1264
1265     parse_cmd_line(ctl_line);
1266     g_free(ctl_line);
1267
1268     return TRUE;
1269 }
1270
1271 static void
1272 create_stdin () {
1273     GIOChannel *chan = NULL;
1274     GError *error = NULL;
1275
1276     chan = g_io_channel_unix_new(fileno(stdin));
1277     if (chan) {
1278         if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1279             g_error ("Stdin: could not add watch\n");
1280         } else {
1281             if (uzbl.state.verbose)
1282                 printf ("Stdin: watch added successfully\n");
1283         }
1284     } else {
1285         g_error ("Stdin: Error while opening: %s\n", error->message);
1286     }
1287     if (error) g_error_free (error);
1288 }
1289
1290 static gboolean
1291 control_socket(GIOChannel *chan) {
1292     struct sockaddr_un remote;
1293     char buffer[512], *ctl_line;
1294     char temp[128];
1295     int sock, clientsock, n, done;
1296     unsigned int t;
1297
1298     sock = g_io_channel_unix_get_fd(chan);
1299
1300     memset (buffer, 0, sizeof (buffer));
1301
1302     t          = sizeof (remote);
1303     clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1304
1305     done = 0;
1306     do {
1307         memset (temp, 0, sizeof (temp));
1308         n = recv (clientsock, temp, 128, 0);
1309         if (n == 0) {
1310             buffer[strlen (buffer)] = '\0';
1311             done = 1;
1312         }
1313         if (!done)
1314             strcat (buffer, temp);
1315     } while (!done);
1316
1317     if (strcmp (buffer, "\n") < 0) {
1318         buffer[strlen (buffer) - 1] = '\0';
1319     } else {
1320         buffer[strlen (buffer)] = '\0';
1321     }
1322     close (clientsock);
1323     ctl_line = g_strdup(buffer);
1324     parse_cmd_line (ctl_line);
1325
1326 /*
1327    TODO: we should be able to do it with this.  but glib errors out with "Invalid argument"
1328     GError *error = NULL;
1329     gsize len;
1330     GIOStatus ret;
1331     ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1332     if (ret == G_IO_STATUS_ERROR)
1333         g_error ("Error reading: %s\n", error->message);
1334
1335     printf("Got line %s (%u bytes) \n",ctl_line, len);
1336     if(ctl_line) {
1337        parse_line(ctl_line);
1338 */
1339
1340     g_free(ctl_line);
1341     return TRUE;
1342 }
1343
1344 static gchar*
1345 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1346     if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1347         if (unlink(uzbl.comm.socket_path) == -1)
1348             g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1349         g_free(uzbl.comm.socket_path);
1350         uzbl.comm.socket_path = NULL;
1351     }
1352
1353     if (*dir == ' ') {
1354         g_free(dir);
1355         return NULL;
1356     }
1357
1358     GIOChannel *chan = NULL;
1359     int sock, len;
1360     struct sockaddr_un local;
1361     gchar *path = build_stream_name(SOCKET, dir);
1362
1363     sock = socket (AF_UNIX, SOCK_STREAM, 0);
1364
1365     local.sun_family = AF_UNIX;
1366     strcpy (local.sun_path, path);
1367     unlink (local.sun_path);
1368
1369     len = strlen (local.sun_path) + sizeof (local.sun_family);
1370     if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1371         if (uzbl.state.verbose)
1372             printf ("init_socket: opened in %s\n", path);
1373         listen (sock, 5);
1374
1375         if( (chan = g_io_channel_unix_new(sock)) ) {
1376             g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1377             uzbl.comm.socket_path = path;
1378             return dir;
1379         }
1380     } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1381
1382     /* if we got this far, there was an error; cleanup */
1383     g_free(path);
1384     g_free(dir);
1385     return NULL;
1386 }
1387
1388 /*
1389  NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1390  it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1391 */
1392 // this function may be called very early when the templates are not set (yet), hence the checks
1393 static void
1394 update_title (void) {
1395     Behaviour *b = &uzbl.behave;
1396     gchar *parsed;
1397
1398     if (b->show_status) {
1399         if (b->title_format_short) {
1400             parsed = expand_template(b->title_format_short);
1401             gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1402             g_free(parsed);
1403         }
1404         if (b->status_format) {
1405             parsed = expand_template(b->status_format);
1406             gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1407             g_free(parsed);
1408         }
1409         if (b->status_background) {
1410             GdkColor color;
1411             gdk_color_parse (b->status_background, &color);
1412             //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)
1413             gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1414         }
1415     } else {
1416         if (b->title_format_long) {
1417             parsed = expand_template(b->title_format_long);
1418             gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1419             g_free(parsed);
1420         }
1421     }
1422 }
1423
1424 static gboolean
1425 key_press_cb (WebKitWebView* page, GdkEventKey* event)
1426 {
1427     //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1428
1429     (void) page;
1430
1431     if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1432         || 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)
1433         return FALSE;
1434
1435     /* turn off insert mode (if always_insert_mode is not used) */
1436     if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1437         uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1438         update_title();
1439         return TRUE;
1440     }
1441
1442     if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1443         return FALSE;
1444
1445     if (event->keyval == GDK_Escape) {
1446         g_string_truncate(uzbl.state.keycmd, 0);
1447         update_title();
1448         return TRUE;
1449     }
1450
1451     //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1452     if (event->keyval == GDK_Insert) {
1453         gchar * str;
1454         if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1455             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1456         } else {
1457             str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1458         }
1459         if (str) {
1460             g_string_append (uzbl.state.keycmd, str);
1461             update_title ();
1462             free (str);
1463         }
1464         return TRUE;
1465     }
1466
1467     if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1468         g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1469         update_title();
1470     }
1471
1472     gboolean key_ret = FALSE;
1473     if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1474         key_ret = TRUE;
1475     if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1476
1477     run_keycmd(key_ret);
1478     update_title();
1479     if (key_ret) return (!uzbl.behave.insert_mode);
1480     return TRUE;
1481 }
1482
1483 static void
1484 run_keycmd(const gboolean key_ret) {
1485     /* run the keycmd immediately if it isn't incremental and doesn't take args */
1486     Action *action;
1487     if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1488         g_string_truncate(uzbl.state.keycmd, 0);
1489         parse_command(action->name, action->param);
1490         return;
1491     }
1492
1493     /* try if it's an incremental keycmd or one that takes args, and run it */
1494     GString* short_keys = g_string_new ("");
1495     GString* short_keys_inc = g_string_new ("");
1496     unsigned int i;
1497     for (i=0; i<(uzbl.state.keycmd->len); i++) {
1498         g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1499         g_string_assign(short_keys_inc, short_keys->str);
1500         g_string_append_c(short_keys, '_');
1501         g_string_append_c(short_keys_inc, '*');
1502
1503         gboolean exec_now = FALSE;
1504         if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1505             if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1506         } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1507             if (key_ret) { /* just quit the incremental command on return */
1508                 g_string_truncate(uzbl.state.keycmd, 0);
1509                 break;
1510             } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1511         }
1512
1513         if (exec_now) {
1514             GString* parampart = g_string_new (uzbl.state.keycmd->str);
1515             GString* actionname = g_string_new ("");
1516             GString* actionparam = g_string_new ("");
1517             g_string_erase (parampart, 0, i+1);
1518             if (action->name)
1519                 g_string_printf (actionname, action->name, parampart->str);
1520             if (action->param)
1521                 g_string_printf (actionparam, action->param, parampart->str);
1522             parse_command(actionname->str, actionparam->str);
1523             g_string_free (actionname, TRUE);
1524             g_string_free (actionparam, TRUE);
1525             g_string_free (parampart, TRUE);
1526             if (key_ret)
1527                 g_string_truncate(uzbl.state.keycmd, 0);
1528             break;
1529         }
1530
1531         g_string_truncate(short_keys, short_keys->len - 1);
1532     }
1533     g_string_free (short_keys, TRUE);
1534     g_string_free (short_keys_inc, TRUE);
1535 }
1536
1537 static GtkWidget*
1538 create_browser () {
1539     GUI *g = &uzbl.gui;
1540
1541     GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1542     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
1543
1544     g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1545     gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1546
1547     g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1548     g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1549     g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1550     g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
1551     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1552     g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1553     g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1554     g_signal_connect (G_OBJECT (g->web_view), "key-press-event", G_CALLBACK (key_press_cb), g->web_view);
1555     g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1556     g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1557     g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1558
1559     return scrolled_window;
1560 }
1561
1562 static GtkWidget*
1563 create_mainbar () {
1564     GUI *g = &uzbl.gui;
1565
1566     g->mainbar = gtk_hbox_new (FALSE, 0);
1567
1568     g->mainbar_label = gtk_label_new ("");
1569     gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1570     gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1571     gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1572     gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1573     gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1574     return g->mainbar;
1575 }
1576
1577 static
1578 GtkWidget* create_window () {
1579     GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1580     gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1581     gtk_widget_set_name (window, "Uzbl browser");
1582     g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1583
1584     return window;
1585 }
1586
1587 static void
1588 run_handler (const gchar *act, const gchar *args) {
1589     char **parts = g_strsplit(act, " ", 2);
1590     if (!parts) return;
1591     else if ((g_strcmp0(parts[0], "spawn") == 0)
1592              || (g_strcmp0(parts[0], "sh") == 0)) {
1593         guint i;
1594         GString *a = g_string_new ("");
1595         char **spawnparts;
1596         spawnparts = split_quoted(parts[1], FALSE);
1597         g_string_append_printf(a, "%s", spawnparts[0]);
1598         if (args) g_string_append_printf(a, " %s", args); /* append handler args before user args */
1599         for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
1600             g_string_append_printf(a, " %s", spawnparts[i]);
1601         parse_command(parts[0], a->str);
1602         g_string_free (a, TRUE);
1603         g_strfreev (spawnparts);
1604     } else
1605         parse_command(parts[0], parts[1]);
1606     g_strfreev (parts);
1607 }
1608
1609 static void
1610 add_binding (const gchar *key, const gchar *act) {
1611     char **parts = g_strsplit(act, " ", 2);
1612     Action *action;
1613
1614     if (!parts)
1615         return;
1616
1617     //Debug:
1618     if (uzbl.state.verbose)
1619         printf ("Binding %-10s : %s\n", key, act);
1620
1621     action = new_action(parts[0], parts[1]);
1622     g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
1623
1624     g_strfreev(parts);
1625 }
1626
1627 static gchar*
1628 get_xdg_var (XDG_Var xdg) {
1629     const gchar* actual_value = getenv (xdg.environmental);
1630     const gchar* home         = getenv ("HOME");
1631
1632     gchar* return_value = str_replace ("~", home, actual_value);
1633
1634     if (! actual_value || strcmp (actual_value, "") == 0) {
1635         if (xdg.default_value) {
1636             return_value = str_replace ("~", home, xdg.default_value);
1637         } else {
1638             return_value = NULL;
1639         }
1640     }
1641     return return_value;
1642 }
1643
1644 static gchar*
1645 find_xdg_file (int xdg_type, char* filename) {
1646     /* xdg_type = 0 => config
1647        xdg_type = 1 => data
1648        xdg_type = 2 => cache*/
1649
1650     gchar* temporary_file   = malloc (1024);
1651     gchar* temporary_string = NULL;
1652     char*  saveptr;
1653     char*  buf;
1654
1655     buf = get_xdg_var (XDG[xdg_type]);
1656     strcpy (temporary_file, buf);
1657     strcat (temporary_file, filename);
1658     free(buf);
1659
1660     if (! file_exists (temporary_file) && xdg_type != 2) {
1661         buf = get_xdg_var (XDG[3 + xdg_type]);
1662         temporary_string = (char *) strtok_r (buf, ":", &saveptr);
1663         free(buf);
1664
1665         while (temporary_string && ! file_exists (temporary_file)) {
1666             strcpy (temporary_file, temporary_string);
1667             strcat (temporary_file, filename);
1668             temporary_string = (char * ) strtok_r (NULL, ":", &saveptr);
1669         }
1670     }
1671
1672     if (file_exists (temporary_file)) {
1673         return temporary_file;
1674     } else {
1675         return NULL;
1676     }
1677 }
1678
1679 static void
1680 settings_init () {
1681     State *s = &uzbl.state;
1682     Network *n = &uzbl.net;
1683
1684     uzbl.behave.reset_command_mode = 1;
1685
1686     if (!s->config_file) {
1687         s->config_file = find_xdg_file (0, "/uzbl/config");
1688     }
1689
1690     if (s->config_file) {
1691         GIOChannel *chan = NULL;
1692         gchar *readbuf = NULL;
1693         gsize len;
1694
1695         chan = g_io_channel_new_file(s->config_file, "r", NULL);
1696
1697         if (chan) {
1698             while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL)
1699                     == G_IO_STATUS_NORMAL) {
1700                 parse_cmd_line(readbuf);
1701                 g_free (readbuf);
1702             }
1703
1704             g_io_channel_unref (chan);
1705             if (uzbl.state.verbose)
1706                 printf ("Config %s loaded\n", s->config_file);
1707         } else {
1708             fprintf(stderr, "uzbl: error loading file%s\n", s->config_file);
1709         }
1710     } else {
1711         if (uzbl.state.verbose)
1712             printf ("No configuration file loaded.\n");
1713     }
1714     if (!uzbl.behave.status_format)
1715         set_var_value("status_format", STATUS_DEFAULT);
1716     if (!uzbl.behave.title_format_long)
1717         set_var_value("title_format_long", TITLE_LONG_DEFAULT);
1718     if (!uzbl.behave.title_format_short)
1719         set_var_value("title_format_short", TITLE_SHORT_DEFAULT);
1720
1721
1722     g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1723 }
1724
1725 static gchar*
1726 set_useragent(gchar *val) {
1727     if (*val == ' ') {
1728         g_free(val);
1729         return NULL;
1730     }
1731     gchar *ua = expand_template(val);
1732     if (ua)
1733         g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1734     return ua;
1735 }
1736
1737 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1738     (void) session;
1739     (void) user_data;
1740     if (!uzbl.behave.cookie_handler) return;
1741
1742     gchar * stdout = NULL;
1743     soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1744     GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1745     gchar *action = g_strdup ("GET");
1746     SoupURI * soup_uri = soup_message_get_uri(msg);
1747     sharg_append(a, action);
1748     sharg_append(a, soup_uri->host);
1749     sharg_append(a, soup_uri->path);
1750     run_command(uzbl.behave.cookie_handler, 0, a->data, TRUE, &stdout); /* TODO: use handler */
1751     //run_handler(uzbl.behave.cookie_handler); /* TODO: global stdout pointer, spawn_sync */
1752     if(stdout) {
1753         soup_message_headers_replace (msg->request_headers, "Cookie", stdout);
1754     }
1755     g_free (action);
1756     g_array_free(a, TRUE);
1757 }
1758
1759 static void
1760 save_cookies (SoupMessage *msg, gpointer user_data){
1761     (void) user_data;
1762     GSList *ck;
1763     char *cookie;
1764     for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1765         cookie = soup_cookie_to_set_cookie_header(ck->data);
1766         GArray *a = g_array_new(TRUE, FALSE, sizeof(gchar*));
1767         SoupURI * soup_uri = soup_message_get_uri(msg);
1768         gchar *action = strdup("PUT");
1769         sharg_append(a, action);
1770         sharg_append(a, soup_uri->host);
1771         sharg_append(a, soup_uri->path);
1772         sharg_append(a, cookie);
1773         run_command(uzbl.behave.cookie_handler, 0, a->data, FALSE, NULL);
1774         g_free (cookie);
1775         g_free (action);
1776         g_array_free(a, TRUE);
1777     }
1778     g_slist_free(ck);
1779 }
1780
1781
1782 int
1783 main (int argc, char* argv[]) {
1784     gtk_init (&argc, &argv);
1785     if (!g_thread_supported ())
1786         g_thread_init (NULL);
1787
1788     uzbl.state.executable_path = g_strdup(argv[0]);
1789     uzbl.state.selected_url = NULL;
1790     uzbl.state.searchtx = NULL;
1791
1792     GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
1793     g_option_context_add_main_entries (context, entries, NULL);
1794     g_option_context_add_group (context, gtk_get_option_group (TRUE));
1795     g_option_context_parse (context, &argc, &argv, NULL);
1796     g_option_context_free(context);
1797     /* initialize hash table */
1798     uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
1799
1800     uzbl.net.soup_session = webkit_get_default_session();
1801     uzbl.state.keycmd = g_string_new("");
1802
1803     if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
1804         fprintf(stderr, "uzbl: error hooking SIGTERM\n");
1805     if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
1806         fprintf(stderr, "uzbl: error hooking SIGINT\n");
1807
1808     if(uname(&uzbl.state.unameinfo) == -1)
1809         g_printerr("Can't retrieve unameinfo.  Your useragent might appear wrong.\n");
1810
1811     setup_regex();
1812     setup_scanner();
1813     commands_hash ();
1814     make_var_to_name_hash();
1815
1816
1817     uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
1818
1819     uzbl.gui.scrolled_win = create_browser();
1820     create_mainbar();
1821
1822     /* initial packing */
1823     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1824     gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1825
1826     uzbl.gui.main_window = create_window ();
1827     gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
1828
1829
1830     gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1831     gtk_widget_show_all (uzbl.gui.main_window);
1832     uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
1833
1834     if (uzbl.state.verbose) {
1835         printf("Uzbl start location: %s\n", argv[0]);
1836         printf("window_id %i\n",(int) uzbl.xwin);
1837         printf("pid %i\n", getpid ());
1838         printf("name: %s\n", uzbl.state.instance_name);
1839     }
1840
1841     uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
1842     uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
1843     uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
1844     uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
1845     gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
1846
1847     settings_init ();
1848
1849     if (!uzbl.behave.show_status)
1850         gtk_widget_hide(uzbl.gui.mainbar);
1851     else
1852         update_title();
1853
1854     create_stdin();
1855
1856     if(uzbl.state.uri)
1857         load_uri (uzbl.gui.web_view, uzbl.state.uri);
1858
1859     gtk_main ();
1860     clean_up();
1861
1862     return EXIT_SUCCESS;
1863 }
1864
1865 /* vi: set et ts=4: */