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