[pan2: 227/268] + invert selection + gnome-keyring implementation + preparations for yenc glib changes



commit 70dac2530883b844f8cbf031ff677e17d9fbc53f
Author: Heinrich MÃller <henmull src gnome org>
Date:   Tue Dec 6 03:30:50 2011 +0100

    + invert selection
    + gnome-keyring implementation
    + preparations for yenc glib changes

 configure.in                        |   16 +-
 gtkspell/examples/advanced.c        |   82 ++
 gtkspell/examples/simple.c          |   58 +
 gtkspell/gtkspell/deprecated.c      |   35 +
 gtkspell/gtkspell/gtkspell.c        |  888 ++++++++++++
 pan/data-impl/Makefile.am           |    8 +-
 pan/data-impl/data-impl.cc          |  115 ++
 pan/data-impl/data-impl.h           |   19 +
 pan/data-impl/server.cc             |   35 +-
 pan/data/data.h                     |   16 +-
 pan/gui/Makefile.am                 |    5 +-
 pan/gui/actions.cc                  |   20 +-
 pan/gui/dl-headers-ui.cc            |    2 +-
 pan/gui/gui.cc                      |   15 +
 pan/gui/gui.h                       |    2 +
 pan/gui/header-pane.cc              |   37 +-
 pan/gui/header-pane.h               |    4 +-
 pan/gui/pan-ui.h                    |    2 +
 pan/gui/pan.cc                      |    6 +
 pan/gui/pan.ui.h                    |    8 +
 pan/gui/server-ui.cc                |   10 +-
 pan/icons/icon_get_flagged.png      |  Bin 935 -> 1128 bytes
 pan/tasks/Makefile.am               |    2 +
 pan/tasks/nntp-pool.cc              |    5 +-
 pan/tasks/nntp.cc                   |   88 ++-
 pan/tasks/nntp.h                    |   22 +-
 pan/tasks/task-xoverinfo.cc         |  208 +++
 pan/tasks/task-xoverinfo.h          |   90 ++
 pan/usenet-utils/Makefile.am        |    7 +-
 pan/usenet-utils/blowfish.cc        |  229 +++
 pan/usenet-utils/blowfish.h         |   79 +
 pan/usenet-utils/blowfish_cyphers.h |  268 ++++
 yenclib/crc32.c                     |  614 ++++++++
 yenclib/crc32.h                     |   38 +
 yenclib/uuencode.c                  | 2692 +++++++++++++++++++++++++++++++++++
 yenclib/uunconc.c                   | 1718 ++++++++++++++++++++++
 36 files changed, 7413 insertions(+), 30 deletions(-)
---
diff --git a/configure.in b/configure.in
index 6ab3e39..b95f646 100644
--- a/configure.in
+++ b/configure.in
@@ -53,6 +53,7 @@ GTKSPELL_REQUIRED=2.0.7
 OPENSSL_REQUIRED=1.0.0
 LIBNOTIFY_REQUIRED=0.4.1
 LIBGSASL_REQUIRED=1.6.1
+LIBGKR_REQUIRED=6.2.2
 AC_SUBST(GLIB_REQUIRED)
 AC_SUBST(GMIME_REQUIRED)
 AC_SUBST(GTK_REQUIRED)
@@ -60,6 +61,7 @@ AC_SUBST(GTKSPELL_REQUIRED)
 AC_SUBST(OPENSSL_REQUIRED)
 AC_SUBST(LIBNOTIFY_REQUIRED)
 AC_SUBST(LIBGSASL_REQUIRED)
+AC_SUBST(LIBGKR_REQUIRED)
 
 AC_PROG_CXX
 AC_HEADER_STDC
@@ -137,6 +139,14 @@ if test "x$HAVE_SASL" = "xyes"; then
   AC_DEFINE([HAVE_SASL],[1],[libgsasl for secure NNTP authentication])
 fi
 
+dnl Check for gnome-keyring for password storage
+PKG_CHECK_MODULES([LIBGNOME_KEYRING_1],[gnome-keyring-1 >= $LIBGKR_REQUIRED],[HAVE_GKR="yes"],[HAVE_GKR="no"])
+AC_SUBST([LIBGNOME_KEYRING_1_CFLAGS])
+AC_SUBST([LIBGNOME_KEYRING_1_LIBS])
+if test "x$HAVE_GKR" = "xyes"; then
+  AC_DEFINE([HAVE_GKR],[1],[gnome-keyring-1 for password storage])
+fi
+
 
 dnl Check to see if strftime supports the use of %l and %k
 AC_MSG_CHECKING(for %l and %k support in strftime)
@@ -188,9 +198,9 @@ case $host_os in
 esac
 AM_CONDITIONAL([HAVE_WIN32],[test "$win32" = "yes"])
 
-CXXFLAGS="$CXXFLAGS -g"
-CPPFLAGS="$CPPFLAGS -g"
-CFLAGS="$CFLAGS -g"
+CXXFLAGS="$CXXFLAGS -g -I/usr/lib/"
+CPPFLAGS="$CPPFLAGS -g -I/usr/lib/"
+CFLAGS="$CFLAGS -g -I/usr/lib/"
 
 dnl build the output files
 AC_SUBST(panlocaledir)
diff --git a/gtkspell/examples/advanced.c b/gtkspell/examples/advanced.c
new file mode 100644
index 0000000..1ce2273
--- /dev/null
+++ b/gtkspell/examples/advanced.c
@@ -0,0 +1,82 @@
+/* vim: set ts=4 sw=4 wm=5 : */
+
+/* This example demonstrates detaching and reattaching GtkSpell. */
+
+#include <gtk/gtk.h>
+#include <gtkspell/gtkspell.h>
+
+GtkWidget *window, *attached, *view;
+
+static void
+report_gtkspell_error(const char *err) {
+	GtkWidget *dlg;
+	dlg = gtk_message_dialog_new(
+			GTK_WINDOW(window),
+			GTK_DIALOG_DESTROY_WITH_PARENT,
+			GTK_MESSAGE_ERROR,
+			GTK_BUTTONS_CLOSE,
+			"GtkSpell error: %s", err);
+	gtk_dialog_run(GTK_DIALOG(dlg));
+	gtk_widget_destroy(dlg);
+}
+
+static void
+attach_cb() {
+	GtkSpell *spell;
+	GError *error = NULL;
+
+	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(attached))) {
+		spell = gtkspell_new_attach(GTK_TEXT_VIEW(view), NULL, &error);
+
+		if (spell == NULL) {
+			report_gtkspell_error(error->message);
+			g_error_free(error);
+		}
+	} else {
+		gtkspell_detach(gtkspell_get_from_text_view(GTK_TEXT_VIEW(view)));
+	}
+}
+
+int
+main(int argc, char* argv[]) {
+	GtkWidget *box, *hbox, *scroll;
+
+	gtk_init(&argc, &argv);
+
+	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+
+	view = gtk_text_view_new();
+	gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(view), GTK_WRAP_WORD);
+
+	scroll = gtk_scrolled_window_new(NULL, NULL);
+	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), 
+			GTK_POLICY_AUTOMATIC,
+			GTK_POLICY_AUTOMATIC);
+	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll),
+			GTK_SHADOW_IN);
+	gtk_container_add(GTK_CONTAINER(scroll), view);
+
+	hbox = gtk_hbox_new(FALSE, 5);
+	attached = gtk_toggle_button_new_with_label("Attached");
+	g_signal_connect(G_OBJECT(attached), "toggled",
+			G_CALLBACK(attach_cb), NULL);
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(attached), TRUE);
+	gtk_box_pack_start(GTK_BOX(hbox), attached, FALSE, FALSE, 0);
+
+	box = gtk_vbox_new(FALSE, 5);
+	gtk_box_pack_start(GTK_BOX(box), scroll, TRUE, TRUE, 0);
+	gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
+	gtk_widget_show_all(box);
+
+	gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
+	gtk_window_set_title(GTK_WINDOW(window), "\"Advanced\" GtkSpell Demonstration");
+	gtk_container_set_border_width(GTK_CONTAINER(window), 10);
+	g_signal_connect(G_OBJECT(window), "delete-event",
+			G_CALLBACK(gtk_main_quit), NULL);
+	gtk_container_add(GTK_CONTAINER(window), box);
+
+	gtk_widget_show(window);
+	gtk_main();
+
+	return 0;
+}
diff --git a/gtkspell/examples/simple.c b/gtkspell/examples/simple.c
new file mode 100644
index 0000000..cc454f1
--- /dev/null
+++ b/gtkspell/examples/simple.c
@@ -0,0 +1,58 @@
+/* vim: set ts=4 sw=4 wm=5 : */
+
+#include <gtk/gtk.h>
+#include <gtkspell/gtkspell.h>
+
+int
+main(int argc, char* argv[]) {
+	GtkWidget *win, *box, *scroll, *view;
+	GError *error = NULL;
+	char *errortext = NULL;
+
+	gtk_init(&argc, &argv);
+
+	view = gtk_text_view_new();
+	gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(view), GTK_WRAP_WORD);
+
+	if (gtkspell_new_attach(GTK_TEXT_VIEW(view), NULL, &error) == NULL) {
+		g_print("gtkspell error: %s\n", error->message);
+		errortext = g_strdup_printf("GtkSpell was unable to initialize.\n"
+				"%s", error->message);
+		g_error_free(error);
+	}
+
+	scroll = gtk_scrolled_window_new(NULL, NULL);
+	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), 
+			GTK_POLICY_AUTOMATIC,
+			GTK_POLICY_AUTOMATIC);
+	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll),
+			GTK_SHADOW_IN);
+	gtk_container_add(GTK_CONTAINER(scroll), view);
+
+	box = gtk_vbox_new(FALSE, 5);
+	if (errortext) {
+		gtk_box_pack_start(GTK_BOX(box), gtk_label_new(errortext),
+				FALSE, FALSE, 0);
+		g_free(errortext);
+	} else {
+		gtk_box_pack_start(GTK_BOX(box),
+				gtk_label_new("Type some text into the text box.\n"
+					"Try misspelling some words.  Then right-click on them."),
+				FALSE, FALSE, 0);
+	}
+	gtk_box_pack_start(GTK_BOX(box), scroll, TRUE, TRUE, 0);
+	gtk_widget_show_all(box);
+
+	win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	gtk_window_set_default_size(GTK_WINDOW(win), 400, 300);
+	gtk_window_set_title(GTK_WINDOW(win), "Simple GtkSpell Demonstration");
+	gtk_container_set_border_width(GTK_CONTAINER(win), 10);
+	g_signal_connect(G_OBJECT(win), "delete-event",
+			G_CALLBACK(gtk_main_quit), NULL);
+	gtk_container_add(GTK_CONTAINER(win), box);
+
+	gtk_widget_show(win);
+	gtk_main();
+
+	return 0;
+}
diff --git a/gtkspell/gtkspell/deprecated.c b/gtkspell/gtkspell/deprecated.c
new file mode 100644
index 0000000..7fe93e2
--- /dev/null
+++ b/gtkspell/gtkspell/deprecated.c
@@ -0,0 +1,35 @@
+/* gtkspell - a spell-checking addon for GTK's TextView widget
+ * Copyright (c) 2002 Evan Martin.
+ */
+
+/* vim: set ts=4 sw=4 wm=5 : */
+
+#include <gtk/gtk.h>
+#include "gtkspell.h"
+
+/**
+ * gtkspell_init:
+ *
+ * This function is deprecated and included only for backward
+ * compatibility. 
+ *
+ * Returns: nothing.
+ */
+int
+gtkspell_init() {
+	/* we do nothing. */
+	return 0;
+}
+
+/**
+ * gtkspell_attach:
+ * @view: a #GtkTextView.
+ *
+ * This function is deprecated and included only for backward
+ * compatibility.  It calls gtkspell_new_attach() with the default language
+ * and without error handling.
+ */
+void
+gtkspell_attach(GtkTextView *view) {
+	gtkspell_new_attach(view, NULL, NULL);
+}
diff --git a/gtkspell/gtkspell/gtkspell.c b/gtkspell/gtkspell/gtkspell.c
new file mode 100644
index 0000000..ecca064
--- /dev/null
+++ b/gtkspell/gtkspell/gtkspell.c
@@ -0,0 +1,888 @@
+/* gtkspell - a spell-checking addon for GTK's TextView widget
+ * Copyright (c) 2002 Evan Martin.
+ */
+
+/* vim: set ts=4 sw=4 wm=5 : */
+
+#include <string.h>
+#include <gtk/gtk.h>
+#include <libintl.h>
+#include <locale.h>
+#include "../config.h"
+#include "gtkspell.h"
+
+#define _(String) dgettext (PACKAGE, String)
+
+#define GTKSPELL_MISSPELLED_TAG "gtkspell-misspelled"
+
+#include <enchant.h>
+
+static const int debug = 0;
+static const int quiet = 0;
+
+static EnchantBroker *broker = NULL;
+static int broker_ref_cnt;
+
+struct _GtkSpell {
+	GtkTextView *view;
+	GtkTextBuffer *buffer;
+	GtkTextTag *tag_highlight;
+	GtkTextMark *mark_insert_start;
+	GtkTextMark *mark_insert_end;
+	gboolean deferred_check;
+	EnchantDict *speller;
+	GtkTextMark *mark_click;
+	gchar *lang;
+};
+
+static void gtkspell_free(GtkSpell *spell);
+
+#define GTKSPELL_OBJECT_KEY "gtkspell"
+
+GQuark
+gtkspell_error_quark(void) {
+	static GQuark q = 0;
+	if (q == 0)
+		q = g_quark_from_static_string("gtkspell-error-quark");
+	return q;
+}
+
+static gboolean
+gtkspell_text_iter_forward_word_end(GtkTextIter *i) {
+	GtkTextIter iter;
+
+/* heuristic: 
+ * if we're on an singlequote/apostrophe and
+ * if the next letter is alphanumeric,
+ * this is an apostrophe. */
+
+	if (!gtk_text_iter_forward_word_end(i))
+		return FALSE;
+
+	if (gtk_text_iter_get_char(i) != '\'')
+		return TRUE;
+
+	iter = *i;
+	if (gtk_text_iter_forward_char(&iter)) {
+		if (g_unichar_isalpha(gtk_text_iter_get_char(&iter))) {
+			return (gtk_text_iter_forward_word_end(i));
+		}
+	}
+
+	return TRUE;
+}
+
+static gboolean
+gtkspell_text_iter_backward_word_start(GtkTextIter *i) {
+	GtkTextIter iter;
+
+	if (!gtk_text_iter_backward_word_start(i))
+		return FALSE;
+
+	iter = *i;
+	if (gtk_text_iter_backward_char(&iter)) {
+		if (gtk_text_iter_get_char(&iter) == '\'') {
+			if (gtk_text_iter_backward_char(&iter)) {
+				if (g_unichar_isalpha(gtk_text_iter_get_char(&iter))) {
+					return (gtk_text_iter_backward_word_start(i));
+				}
+			}
+		}
+	}
+
+	return TRUE;
+}
+
+#define gtk_text_iter_backward_word_start gtkspell_text_iter_backward_word_start
+#define gtk_text_iter_forward_word_end gtkspell_text_iter_forward_word_end
+
+static void
+check_word(GtkSpell *spell, GtkTextBuffer *buffer,
+           GtkTextIter *start, GtkTextIter *end) {
+	char *text;
+	if (!spell->speller)
+		return;
+	text = gtk_text_buffer_get_text(buffer, start, end, FALSE);
+	if (debug) g_print("checking: %s\n", text);
+	if (g_unichar_isdigit(*text) == FALSE) /* don't check numbers */
+		if (enchant_dict_check(spell->speller, text, strlen(text)) != 0)
+			gtk_text_buffer_apply_tag(buffer, spell->tag_highlight, start, end);
+	g_free(text);
+}
+
+static void
+print_iter(char *name, GtkTextIter *iter) {
+	g_print("%1s[%d%c%c%c] ", name, gtk_text_iter_get_offset(iter),
+		gtk_text_iter_starts_word(iter) ? 's' : ' ',
+		gtk_text_iter_inside_word(iter) ? 'i' : ' ',
+		gtk_text_iter_ends_word(iter) ? 'e' : ' ');
+}
+
+static void
+check_range(GtkSpell *spell, GtkTextBuffer *buffer,
+            GtkTextIter start, GtkTextIter end, gboolean force_all) {
+	/* we need to "split" on word boundaries.
+	 * luckily, pango knows what "words" are 
+	 * so we don't have to figure it out. */
+
+	GtkTextIter wstart, wend, cursor, precursor;
+	gboolean inword, highlight;
+	if (debug) {
+		g_print("check_range: "); print_iter("s", &start); print_iter("e", &end); g_print(" -> ");
+	}
+
+	if (gtk_text_iter_inside_word(&end))
+		gtk_text_iter_forward_word_end(&end);
+	if (!gtk_text_iter_starts_word(&start)) {
+		if (gtk_text_iter_inside_word(&start) || 
+				gtk_text_iter_ends_word(&start)) {
+			gtk_text_iter_backward_word_start(&start);
+		} else {
+			/* if we're neither at the beginning nor inside a word,
+			 * me must be in some spaces.
+			 * skip forward to the beginning of the next word. */
+			//gtk_text_buffer_remove_tag(buffer, tag_highlight, &start, &end);
+			if (gtk_text_iter_forward_word_end(&start))
+				gtk_text_iter_backward_word_start(&start);
+		}
+	}
+	gtk_text_buffer_get_iter_at_mark(buffer, &cursor,
+			gtk_text_buffer_get_insert(buffer));
+
+	precursor = cursor;
+	gtk_text_iter_backward_char(&precursor);
+	highlight = gtk_text_iter_has_tag(&cursor, spell->tag_highlight) ||
+			gtk_text_iter_has_tag(&precursor, spell->tag_highlight);
+	
+	gtk_text_buffer_remove_tag(buffer, spell->tag_highlight, &start, &end);
+
+	/* Fix a corner case when replacement occurs at beginning of buffer:
+	 * An iter at offset 0 seems to always be inside a word,
+	 * even if it's not.  Possibly a pango bug.
+	 */
+	if (gtk_text_iter_get_offset(&start) == 0) {
+		gtk_text_iter_forward_word_end(&start);
+		gtk_text_iter_backward_word_start(&start);
+	}
+
+	if (debug) {print_iter("s", &start); print_iter("e", &end); g_print("\n");}
+
+	wstart = start;
+	while (gtk_text_iter_compare(&wstart, &end) < 0) {
+		/* move wend to the end of the current word. */
+		wend = wstart;
+		gtk_text_iter_forward_word_end(&wend);
+
+		inword = (gtk_text_iter_compare(&wstart, &cursor) < 0) && 
+				(gtk_text_iter_compare(&cursor, &wend) <= 0);
+
+		if (inword && !force_all) {
+			/* this word is being actively edited, 
+			 * only check if it's already highligted,
+			 * otherwise defer this check until later. */
+			if (highlight)
+				check_word(spell, buffer, &wstart, &wend);
+			else
+				spell->deferred_check = TRUE;
+		} else {
+			check_word(spell, buffer, &wstart, &wend);
+			spell->deferred_check = FALSE;
+		}
+
+		/* now move wend to the beginning of the next word, */
+		gtk_text_iter_forward_word_end(&wend);
+		gtk_text_iter_backward_word_start(&wend);
+		/* make sure we've actually advanced
+		 * (we don't advance in some corner cases), */
+		if (gtk_text_iter_equal(&wstart, &wend))
+			break; /* we're done in these cases.. */
+		/* and then pick this as the new next word beginning. */
+		wstart = wend;
+	}
+}
+
+static void
+check_deferred_range(GtkSpell *spell, GtkTextBuffer *buffer, gboolean force_all) {
+	GtkTextIter start, end;
+	gtk_text_buffer_get_iter_at_mark(buffer, &start, spell->mark_insert_start);
+	gtk_text_buffer_get_iter_at_mark(buffer, &end, spell->mark_insert_end);
+	check_range(spell, buffer, start, end, force_all);
+}
+
+/* insertion works like this:
+ *  - before the text is inserted, we mark the position in the buffer.
+ *  - after the text is inserted, we see where our mark is and use that and
+ *    the current position to check the entire range of inserted text.
+ *
+ * this may be overkill for the common case (inserting one character). */
+
+static void
+insert_text_before(GtkTextBuffer *buffer, GtkTextIter *iter,
+                   gchar *text, gint len, GtkSpell *spell) {
+	gtk_text_buffer_move_mark(buffer, spell->mark_insert_start, iter);
+}
+
+static void
+insert_text_after(GtkTextBuffer *buffer, GtkTextIter *iter,
+                  gchar *text, gint len, GtkSpell *spell) {
+	GtkTextIter start;
+
+	if (debug) g_print("insert\n");
+
+	/* we need to check a range of text. */
+	gtk_text_buffer_get_iter_at_mark(buffer, &start, spell->mark_insert_start);
+	check_range(spell, buffer, start, *iter, FALSE);
+	
+	gtk_text_buffer_move_mark(buffer, spell->mark_insert_end, iter);
+}
+
+/* deleting is more simple:  we're given the range of deleted text.
+ * after deletion, the start and end iters should be at the same position
+ * (because all of the text between them was deleted!).
+ * this means we only really check the words immediately bounding the
+ * deletion.
+ */
+
+static void
+delete_range_after(GtkTextBuffer *buffer,
+                   GtkTextIter *start, GtkTextIter *end, GtkSpell *spell) {
+	if (debug) g_print("delete\n");
+	check_range(spell, buffer, *start, *end, FALSE);
+}
+
+static void
+mark_set(GtkTextBuffer *buffer, GtkTextIter *iter, 
+		 GtkTextMark *mark, GtkSpell *spell) {
+	/* if the cursor has moved and there is a deferred check so handle it now */
+	if ((mark == gtk_text_buffer_get_insert(buffer)) && spell->deferred_check)
+		check_deferred_range(spell, buffer, FALSE);
+}
+
+static void
+get_word_extents_from_mark(GtkTextBuffer *buffer,
+                     GtkTextIter *start, GtkTextIter *end, GtkTextMark *mark) {
+	gtk_text_buffer_get_iter_at_mark(buffer, start, mark);
+	if (!gtk_text_iter_starts_word(start)) 
+		gtk_text_iter_backward_word_start(start);
+	*end = *start;
+	if (gtk_text_iter_inside_word(end))
+		gtk_text_iter_forward_word_end(end);
+}
+
+static void
+add_to_dictionary(GtkWidget *menuitem, GtkSpell *spell) {
+	char *word;
+	GtkTextIter start, end;
+
+	get_word_extents_from_mark(spell->buffer, &start, &end, spell->mark_click);
+	word = gtk_text_buffer_get_text(spell->buffer, &start, &end, FALSE);
+	
+	enchant_dict_add_to_pwl( spell->speller, word, strlen(word));
+
+	gtkspell_recheck_all(spell);
+
+	g_free(word);
+}
+
+static void
+ignore_all(GtkWidget *menuitem, GtkSpell *spell) {
+	char *word;
+	GtkTextIter start, end;
+
+	get_word_extents_from_mark(spell->buffer, &start, &end, spell->mark_click);
+	word = gtk_text_buffer_get_text(spell->buffer, &start, &end, FALSE);
+	
+	enchant_dict_add_to_session(spell->speller, word, strlen(word));
+
+	gtkspell_recheck_all(spell);
+
+	g_free(word);
+}
+
+static void
+replace_word(GtkWidget *menuitem, GtkSpell *spell) {
+	char *oldword;
+	const char *newword;
+	GtkTextIter start, end;
+	
+	if (!spell->speller)
+		return;
+
+	get_word_extents_from_mark(spell->buffer, &start, &end, spell->mark_click);
+	oldword = gtk_text_buffer_get_text(spell->buffer, &start, &end, FALSE);
+	newword = gtk_label_get_text(GTK_LABEL
+				     (gtk_bin_get_child(GTK_BIN(menuitem))));
+
+	if (debug) {
+		g_print("old word: '%s'\n", oldword);
+		print_iter("s", &start); print_iter("e", &end);
+		g_print("\nnew word: '%s'\n", newword);
+	}
+
+	gtk_text_buffer_begin_user_action(spell->buffer);
+	gtk_text_buffer_delete(spell->buffer, &start, &end);
+	gtk_text_buffer_insert(spell->buffer, &start, newword, -1);
+	gtk_text_buffer_end_user_action(spell->buffer);
+
+	enchant_dict_store_replacement(spell->speller, 
+			oldword, strlen(oldword),
+			newword, strlen(newword));
+
+	g_free(oldword);
+}
+
+/* This function populates suggestions at the top of the passed menu */
+static void
+add_suggestion_menus(GtkSpell *spell, GtkTextBuffer *buffer,
+                      const char *word, GtkWidget *topmenu) {
+	GtkWidget *menu;
+	GtkWidget *mi;
+	char **suggestions;
+	size_t n_suggs, i;
+	char *label;
+	
+	menu = topmenu;
+
+	if (!spell->speller)
+		return;
+
+	gint menu_position = 0;
+
+	suggestions = enchant_dict_suggest(spell->speller, word, strlen(word), &n_suggs);
+
+	if (suggestions == NULL || !n_suggs) {
+		/* no suggestions.  put something in the menu anyway... */
+		GtkWidget *label;
+		label = gtk_label_new("");
+		gtk_label_set_markup(GTK_LABEL(label), _("<i>(no suggestions)</i>"));
+
+		mi = gtk_menu_item_new();
+		gtk_container_add(GTK_CONTAINER(mi), label);
+		gtk_widget_show_all(mi);
+		gtk_menu_shell_insert(GTK_MENU_SHELL(menu), mi, menu_position++);
+	} else {
+		/* build a set of menus with suggestions. */
+		gboolean inside_more_submenu = FALSE;
+		for (i = 0; i < n_suggs; i++ ) {
+			if (i > 0 && i % 10 == 0) {
+				inside_more_submenu = TRUE;
+				mi = gtk_menu_item_new_with_label(_("More..."));
+				gtk_widget_show(mi);
+				gtk_menu_shell_insert(GTK_MENU_SHELL(menu), mi, menu_position++);
+
+				menu = gtk_menu_new();
+				gtk_menu_item_set_submenu(GTK_MENU_ITEM(mi), menu);
+			}
+			mi = gtk_menu_item_new_with_label(suggestions[i]);
+			g_signal_connect(G_OBJECT(mi), "activate",
+					G_CALLBACK(replace_word), spell);
+			gtk_widget_show(mi);
+			if (inside_more_submenu) gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
+			else gtk_menu_shell_insert(GTK_MENU_SHELL(menu), mi, menu_position++);
+		}
+	}
+
+	if (suggestions)
+		enchant_dict_free_string_list(spell->speller, suggestions);
+
+	/* + Add to Dictionary */
+	label = g_strdup_printf(_("Add \"%s\" to Dictionary"), word);
+	mi = gtk_image_menu_item_new_with_label(label);
+	g_free(label);
+	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), 
+			gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_MENU));
+	g_signal_connect(G_OBJECT(mi), "activate",
+			G_CALLBACK(add_to_dictionary), spell);
+	gtk_widget_show_all(mi);
+	gtk_menu_shell_insert(GTK_MENU_SHELL(topmenu), mi, menu_position++);
+
+	/* - Ignore All */
+	mi = gtk_image_menu_item_new_with_label(_("Ignore All"));
+	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), 
+			gtk_image_new_from_stock(GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU));
+	g_signal_connect(G_OBJECT(mi), "activate",
+			G_CALLBACK(ignore_all), spell);
+	gtk_widget_show_all(mi);
+	gtk_menu_shell_insert(GTK_MENU_SHELL(topmenu), mi, menu_position++);
+}
+
+static GtkWidget*
+build_suggestion_menu(GtkSpell *spell, GtkTextBuffer *buffer,
+                      const char *word) {
+	GtkWidget *topmenu;
+	topmenu = gtk_menu_new();
+	add_suggestion_menus(spell, buffer, word, topmenu);
+
+	return topmenu;
+}
+
+static void
+language_change_callback(GtkCheckMenuItem *mi, GtkSpell* spell) {
+	if (gtk_check_menu_item_get_active(mi)) {
+		GError* error = NULL;
+		gchar *name;
+		g_object_get(G_OBJECT(mi), "name", &name, NULL);
+		gtkspell_set_language(spell, name, &error);
+		g_free(name);
+	}
+}
+
+struct _languages_cb_struct {GList *langs;};
+
+static void
+dict_describe_cb(const char * const lang_tag,
+		 const char * const provider_name,
+		 const char * const provider_desc,
+		 const char * const provider_file,
+		 void * user_data) {
+
+	struct _languages_cb_struct *languages_cb_struct = (struct _languages_cb_struct *)user_data;
+
+	languages_cb_struct->langs = g_list_insert_sorted(
+		languages_cb_struct->langs, g_strdup(lang_tag),
+		(GCompareFunc) strcmp);
+}
+
+static GtkWidget*
+build_languages_menu(GtkSpell *spell) {
+	GtkWidget *active_item = NULL, *menu = gtk_menu_new();
+	GList *langs;
+	GSList *menu_group = NULL;
+
+	struct _languages_cb_struct languages_cb_struct;
+	languages_cb_struct.langs = NULL;
+
+	enchant_broker_list_dicts(broker, dict_describe_cb, &languages_cb_struct);
+
+	langs = languages_cb_struct.langs;
+
+	for (; langs; langs = langs->next) {
+		gchar *lang_tag = langs->data;
+		GtkWidget* mi = gtk_radio_menu_item_new_with_label(menu_group, lang_tag);
+		menu_group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(mi));
+
+		g_object_set(G_OBJECT(mi), "name", lang_tag, NULL);
+		if (strcmp(spell->lang, lang_tag) == 0)
+			active_item = mi;
+		else
+			g_signal_connect(G_OBJECT(mi), "activate",
+				G_CALLBACK(language_change_callback), spell);
+		gtk_widget_show(mi);
+		gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
+
+		g_free(lang_tag);
+	}
+	if (active_item)
+		gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(active_item), TRUE);
+
+	g_list_free(languages_cb_struct.langs);
+
+	return menu;
+}
+
+static void
+populate_popup(GtkTextView *textview, GtkMenu *menu, GtkSpell *spell) {
+	GtkWidget *mi;
+	GtkTextIter start, end;
+	char *word;
+
+	/* menu separator comes first. */
+	mi = gtk_separator_menu_item_new();
+	gtk_widget_show(mi);
+	gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi);
+
+	/* on top: language selection */
+	mi = gtk_menu_item_new_with_label(_("Languages"));
+	gtk_menu_item_set_submenu(GTK_MENU_ITEM(mi), build_languages_menu(spell));
+	gtk_widget_show_all(mi);
+	gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi);
+
+	/* we need to figure out if they picked a misspelled word. */
+	get_word_extents_from_mark(spell->buffer, &start, &end, spell->mark_click);
+
+	/* if our highlight algorithm ever messes up, 
+	 * this isn't correct, either. */
+	if (!gtk_text_iter_has_tag(&start, spell->tag_highlight))
+		return; /* word wasn't misspelled. */
+
+	/* then, on top of it, the suggestions */
+	word = gtk_text_buffer_get_text(spell->buffer, &start, &end, FALSE);
+	add_suggestion_menus(spell, spell->buffer, word, GTK_WIDGET (menu) );
+	g_free(word);
+}
+
+/* when the user right-clicks on a word, they want to check that word.
+ * here, we do NOT  move the cursor to the location of the clicked-upon word
+ * since that prevents the use of edit functions on the context menu. */
+static gboolean
+button_press_event(GtkTextView *view, GdkEventButton *event, GtkSpell *spell) {
+	if (event->button == 3) {
+		gint x, y;
+		GtkTextIter iter;
+
+		/* handle deferred check if it exists */
+		if (spell->deferred_check)
+			check_deferred_range(spell, spell->buffer, TRUE);
+
+		gtk_text_view_window_to_buffer_coords(view, 
+				GTK_TEXT_WINDOW_TEXT, 
+				event->x, event->y,
+				&x, &y);
+		gtk_text_view_get_iter_at_location(view, &iter, x, y);
+		gtk_text_buffer_move_mark(spell->buffer, spell->mark_click, &iter);
+	}
+	return FALSE; /* false: let gtk process this event, too.
+					 we don't want to eat any events. */
+}
+
+/* This event occurs when the popup menu is requested through a key-binding
+ * (Menu Key or <shift>+F10 by default).  In this case we want to set
+ * spell->mark_click to the cursor position. */
+static gboolean
+popup_menu_event(GtkTextView *view, GtkSpell *spell) {
+	GtkTextIter iter;
+
+	gtk_text_buffer_get_iter_at_mark(spell->buffer, &iter, 
+			gtk_text_buffer_get_insert(spell->buffer));
+	gtk_text_buffer_move_mark(spell->buffer, spell->mark_click, &iter);
+	return FALSE; /* false: let gtk process this event, too. */
+}
+
+static void
+_set_lang_from_dict(const char * const lang_tag,
+		    const char * const provider_name,
+		    const char * const provider_desc,
+		    const char * const provider_dll_file,
+		    void * user_data)
+{
+	GtkSpell *spell = user_data;
+
+	g_free(spell->lang);
+	spell->lang = g_strdup(lang_tag);
+}
+
+static gboolean
+gtkspell_set_language_internal(GtkSpell *spell, const gchar *lang, GError **error) {
+	EnchantDict *dict;
+
+	if (lang == NULL) {
+		lang = g_getenv("LANG");
+		if (lang) {
+			if ((strcmp(lang, "C") == 0) || (strcmp(lang, "c") == 0))
+				lang = NULL;
+			else if (lang[0] == 0)
+				lang = NULL;
+		}
+	}
+
+	if (!lang)
+		lang = "en";
+
+	dict = enchant_broker_request_dict(broker, lang);
+
+	if (!dict) {
+		g_set_error(error, GTKSPELL_ERROR, GTKSPELL_ERROR_BACKEND,
+			_("enchant error for language: %s"), lang);
+		return FALSE;
+	}
+
+	if (spell->speller)
+		enchant_broker_free_dict(broker, spell->speller);
+	spell->speller = dict;
+
+	enchant_dict_describe(dict, _set_lang_from_dict, spell);
+
+	return TRUE;
+}
+
+/**
+ * gtkspell_set_language:
+ * @spell:  The #GtkSpell object.
+ * @lang: The language to use, in a form enchant understands (it appears to
+ * be a locale specifier?).
+ * @error: Return location for error.
+ *
+ * Set the language on @spell to @lang, possibily returning an error in
+ * @error.
+ *
+ * Returns: FALSE if there was an error.
+ */
+gboolean
+gtkspell_set_language(GtkSpell *spell, const gchar *lang, GError **error) {
+	gboolean ret;
+
+	if (error)
+		g_return_val_if_fail(*error == NULL, FALSE);
+
+	ret = gtkspell_set_language_internal(spell, lang, error);
+	if (ret)
+		gtkspell_recheck_all(spell);
+
+	return ret;
+}
+
+/**
+ * gtkspell_recheck_all:
+ * @spell:  The #GtkSpell object.
+ *
+ * Recheck the spelling in the entire buffer.
+ */
+void
+gtkspell_recheck_all(GtkSpell *spell) {
+	GtkTextIter start, end;
+
+	gtk_text_buffer_get_bounds(spell->buffer, &start, &end);
+
+	check_range(spell, spell->buffer, start, end, TRUE);
+}
+
+/* changes the buffer
+ * a NULL buffer is acceptable and will only release the current one */
+static void
+gtkspell_set_buffer(GtkSpell *spell, GtkTextBuffer *buffer)
+{
+	GtkTextTagTable *tagtable;
+	GtkTextIter start, end;
+
+	if (spell->buffer) {
+		g_signal_handlers_disconnect_matched(spell->buffer,
+				G_SIGNAL_MATCH_DATA,
+				0, 0, NULL, NULL,
+				spell);
+
+		tagtable = gtk_text_buffer_get_tag_table(spell->buffer);
+
+		gtk_text_buffer_get_bounds(spell->buffer, &start, &end);
+		gtk_text_buffer_remove_tag(spell->buffer, spell->tag_highlight, &start, &end);
+		spell->tag_highlight = NULL;
+
+		gtk_text_buffer_delete_mark(spell->buffer, spell->mark_insert_start);
+		spell->mark_insert_start = NULL;
+		gtk_text_buffer_delete_mark(spell->buffer, spell->mark_insert_end);
+		spell->mark_insert_end = NULL;
+		gtk_text_buffer_delete_mark(spell->buffer, spell->mark_click);
+		spell->mark_click = NULL;
+
+		g_object_unref (spell->buffer);
+	}
+
+	spell->buffer = buffer;
+
+	if (spell->buffer) {
+		g_object_ref (spell->buffer);
+
+		g_signal_connect(G_OBJECT(spell->buffer),
+				"insert-text",
+				G_CALLBACK(insert_text_before), spell);
+		g_signal_connect_after(G_OBJECT(spell->buffer),
+				"insert-text",
+				G_CALLBACK(insert_text_after), spell);
+		g_signal_connect_after(G_OBJECT(spell->buffer),
+				"delete-range",
+				G_CALLBACK(delete_range_after), spell);
+		g_signal_connect(G_OBJECT(spell->buffer),
+				"mark-set",
+				G_CALLBACK(mark_set), spell);
+
+		tagtable = gtk_text_buffer_get_tag_table(spell->buffer);
+		spell->tag_highlight = gtk_text_tag_table_lookup(tagtable, GTKSPELL_MISSPELLED_TAG);
+
+		if (spell->tag_highlight == NULL) {
+			spell->tag_highlight = gtk_text_buffer_create_tag(spell->buffer,
+					GTKSPELL_MISSPELLED_TAG,
+#ifdef HAVE_PANGO_UNDERLINE_ERROR
+					"underline", PANGO_UNDERLINE_ERROR,
+#else
+					"foreground", "red", 
+					"underline", PANGO_UNDERLINE_SINGLE,
+#endif
+					NULL);
+		}
+
+		/* we create the mark here, but we don't use it until text is
+		 * inserted, so we don't really care where iter points.  */
+		gtk_text_buffer_get_bounds(spell->buffer, &start, &end);
+		spell->mark_insert_start = gtk_text_buffer_create_mark(spell->buffer,
+				"gtkspell-insert-start",
+				&start, TRUE);
+		spell->mark_insert_end = gtk_text_buffer_create_mark(spell->buffer,
+				"gtkspell-insert-end",
+				&start, TRUE);
+		spell->mark_click = gtk_text_buffer_create_mark(spell->buffer,
+				"gtkspell-click",
+				&start, TRUE);
+			
+		spell->deferred_check = FALSE;
+
+		/* now check the entire text buffer. */
+		gtkspell_recheck_all(spell);
+	}
+}
+
+static void
+buffer_changed (GtkTextView *view, GParamSpec *pspec, GtkSpell *spell)
+{
+	gtkspell_set_buffer(spell, gtk_text_view_get_buffer(view));
+}
+
+/**
+ * gtkspell_new_attach:
+ * @view: The #GtkTextView to attach to.
+ * @lang: The language to use, in a form pspell understands (it appears to
+ * be a locale specifier?).
+ * @error: Return location for error.
+ *
+ * Create a new #GtkSpell object attached to @view with language @lang.
+ *
+ * Returns: a new #GtkSpell object, or %NULL on error.
+ */
+GtkSpell*
+gtkspell_new_attach(GtkTextView *view, const gchar *lang, GError **error) {
+	GtkSpell *spell;
+
+#ifdef ENABLE_NLS
+	bindtextdomain(PACKAGE, LOCALEDIR);
+	bind_textdomain_codeset(PACKAGE, "UTF-8");
+#endif
+
+	if (error)
+		g_return_val_if_fail(*error == NULL, NULL);
+
+	spell = g_object_get_data(G_OBJECT(view), GTKSPELL_OBJECT_KEY);
+	g_assert(spell == NULL);
+
+	/* We don't need to worry about thread safety.
+	 * Stuff shouldn't be attaching to a GtkTextView from anything other
+	 * than the mainloop thread */
+	if (!broker) {
+		broker = enchant_broker_init();
+		broker_ref_cnt = 0;
+	}
+	broker_ref_cnt++;
+
+
+	/* attach to the widget */
+	spell = g_new0(GtkSpell, 1);
+	spell->view = view;
+	if (!gtkspell_set_language_internal(spell, lang, error)) {
+		broker_ref_cnt--;
+		if (broker_ref_cnt == 0) {
+			enchant_broker_free(broker);
+			broker = NULL;
+		}
+		g_free(spell);
+		return NULL;
+	}
+	g_object_set_data(G_OBJECT(view), GTKSPELL_OBJECT_KEY, spell);
+
+	g_signal_connect_swapped(G_OBJECT(view), "destroy",
+			G_CALLBACK(gtkspell_free), spell);
+	g_signal_connect(G_OBJECT(view), "button-press-event",
+			G_CALLBACK(button_press_event), spell);
+	g_signal_connect(G_OBJECT(view), "populate-popup",
+			G_CALLBACK(populate_popup), spell);
+	g_signal_connect(G_OBJECT(view), "popup-menu",
+			G_CALLBACK(popup_menu_event), spell);
+	g_signal_connect(G_OBJECT(view), "notify::buffer",
+			G_CALLBACK(buffer_changed), spell);
+
+	spell->buffer = NULL;
+	gtkspell_set_buffer(spell, gtk_text_view_get_buffer(view));
+
+	return spell;
+}
+
+static void
+gtkspell_free(GtkSpell *spell) {
+
+	gtkspell_set_buffer(spell, NULL);
+
+	if (broker) {
+		if (spell->speller) {
+			enchant_broker_free_dict(broker, spell->speller);
+		}
+		broker_ref_cnt--;
+		if (broker_ref_cnt == 0) {
+			enchant_broker_free(broker);
+			broker = NULL;
+		}
+	}
+	g_signal_handlers_disconnect_matched(spell->view,
+			G_SIGNAL_MATCH_DATA,
+			0, 0, NULL, NULL,
+			spell);
+	g_free(spell->lang);
+	g_free(spell);
+}
+
+/**
+ * gtkspell_get_from_text_view:
+ * @view: A #GtkTextView.
+ *
+ * Retrieves the #GtkSpell object attached to a text view.
+ *
+ * Returns: the #GtkSpell object, or %NULL if there is no #GtkSpell
+ * attached to @view.
+ */
+GtkSpell*
+gtkspell_get_from_text_view(GtkTextView *view) {
+	return g_object_get_data(G_OBJECT(view), GTKSPELL_OBJECT_KEY);
+}
+
+/**
+ * gtkspell_detach:
+ * @spell: A #GtkSpell.
+ *
+ * Detaches this #GtkSpell from its text view.  Use
+ * gtkspell_get_from_text_view() to retrieve a GtkSpell from a
+ * #GtkTextView.
+ */
+void
+gtkspell_detach(GtkSpell *spell) {
+	g_return_if_fail(spell != NULL);
+
+	g_object_set_data(G_OBJECT(spell->view), GTKSPELL_OBJECT_KEY, NULL);
+	gtkspell_free(spell);
+}
+
+/**
+ * gtkspell_get_suggestions_menu:
+ * @iter: Textiter of position in buffer to be corrected if necessary.
+ *
+ * Retrieves a submenu of replacement spellings, or NULL if the word at @iter is
+ * not misspelt.
+ *
+ * Returns: the #GtkMenu widget, or %NULL if there is no need for a menu
+ */
+GtkWidget*
+gtkspell_get_suggestions_menu(GtkSpell *spell, GtkTextIter *iter) {
+	GtkWidget *submenu = NULL;
+	GtkTextIter start, end;
+
+	g_return_val_if_fail(spell != NULL, NULL);
+
+	/* avoid an empty submenu when enchant is not working properly */
+	if (!spell->speller)
+		return NULL;
+
+	start = *iter;
+	/* use the same lazy test, with same risk, as does the default menu arrangement */
+	if (gtk_text_iter_has_tag(&start, spell->tag_highlight)) {
+		/* word was mis-spelt */
+		gchar *badword;
+		/* in case a fix is requested, move the attention-point */
+		gtk_text_buffer_move_mark(spell->buffer, spell->mark_click, iter);
+		if (!gtk_text_iter_starts_word(&start))
+			gtk_text_iter_backward_word_start(&start);
+		end = start;
+		if (gtk_text_iter_inside_word(&end))
+			gtk_text_iter_forward_word_end(&end);
+		badword = gtk_text_buffer_get_text (spell->buffer, &start, &end, FALSE);
+
+		submenu = build_suggestion_menu (spell, spell->buffer, badword);
+		gtk_widget_show (submenu);
+
+		g_free (badword);
+	}
+	return submenu;
+}
diff --git a/pan/data-impl/Makefile.am b/pan/data-impl/Makefile.am
index 7955688..e080653 100644
--- a/pan/data-impl/Makefile.am
+++ b/pan/data-impl/Makefile.am
@@ -1,4 +1,6 @@
-AM_CPPFLAGS = -I top_srcdir@ @GMIME_CFLAGS@ @GLIB_CFLAGS@ @OPENSSL_CFLAGS@
+AM_CPPFLAGS = -I top_srcdir@ @GMIME_CFLAGS@ @GLIB_CFLAGS@ @OPENSSL_CFLAGS@ @LIBGNOME_KEYRING_1_CFLAGS@
+
+AM_LDFLAGS = @LIBGNOME_KEYRING_1_LIBS@
 
 noinst_LIBRARIES = libpandata.a
 
@@ -13,7 +15,7 @@ libpandata_a_SOURCES = \
  server.cc \
  my-tree.cc \
  task-archive.cc \
- xover.cc 
+ xover.cc
 
 noinst_HEADERS = \
  article-filter.h \
@@ -22,7 +24,7 @@ noinst_HEADERS = \
  data-io.h \
  defgroup.h \
  profiles.h \
- memchunk.h 
+ memchunk.h
 
 #noinst_PROGRAMS = \
 # add-server \
diff --git a/pan/data-impl/data-impl.cc b/pan/data-impl/data-impl.cc
index d285e9e..65e7e73 100644
--- a/pan/data-impl/data-impl.cc
+++ b/pan/data-impl/data-impl.cc
@@ -33,6 +33,9 @@ extern "C" {
 #include <pan/general/time-elapsed.h>
 #include "data-impl.h"
 
+#include <gnome-keyring-1/gnome-keyring.h>
+#include <gnome-keyring-1/gnome-keyring-memory.h>
+
 using namespace pan;
 
 /**
@@ -71,6 +74,8 @@ DataImpl :: DataImpl (bool unit_test, int cache_megs, DataIO * io):
   _descriptions_loaded (false),
   newsrc_autosave_id (0),
   newsrc_autosave_timeout (0)
+//  ,
+//  _blowfish_inited(false)
 {
   rebuild_backend ();
 }
@@ -120,3 +125,113 @@ DataImpl :: save_state ()
   }
 }
 
+
+namespace
+{
+
+  static void
+  stored_password (GnomeKeyringResult res, gpointer user_data)
+  {
+    if (res == GNOME_KEYRING_RESULT_OK)
+    g_print ("password saved successfully!\n");
+    else
+      g_print ("couldn't save password: %s", gnome_keyring_result_to_message (res));
+  }
+
+  static void
+  found_password (GnomeKeyringResult res, const gchar* password, gpointer user_data)
+  {
+    if (res == GNOME_KEYRING_RESULT_OK)
+      g_print ("password found was: %s\n", password);
+    else
+      g_print ("couldn't find password: %s", gnome_keyring_result_to_message (res));
+
+    /* Once this function returns |password| will be freed */
+  }
+
+}
+
+#ifdef HAVE_GKR
+GnomeKeyringResult
+DataImpl :: password_encrypt (const PasswordData& pw)
+{
+  g_return_val_if_fail (gnome_keyring_is_available(), GNOME_KEYRING_RESULT_NO_KEYRING_DAEMON);
+
+  return (
+    gnome_keyring_store_password_sync (
+      GNOME_KEYRING_NETWORK_PASSWORD,
+      GNOME_KEYRING_DEFAULT,
+      _("Pan server password"),
+      pw.pw.str,
+      "user", pw.user.str,
+      "server", pw.server.c_str(),
+      NULL)
+    );
+
+}
+
+GnomeKeyringResult
+DataImpl :: password_decrypt (PasswordData& pw) const
+{
+
+  gchar* pwd(0);
+  g_return_val_if_fail (gnome_keyring_is_available(), GNOME_KEYRING_RESULT_NO_KEYRING_DAEMON);
+
+  GnomeKeyringResult ret =
+    gnome_keyring_find_password_sync (
+    GNOME_KEYRING_NETWORK_PASSWORD,
+    &pwd,
+    "user", pw.user.str,
+    "server", pw.server.c_str(),
+    NULL);
+
+  std::string tmp(pwd);
+  gnome_keyring_free_password(pwd);
+  pw.pw = tmp;
+
+  return ret;
+}
+#endif
+
+//void
+//DataImpl :: blowfish_init ()
+//{
+//
+//  /TODO : Custom key with gtkwidget*
+//  if (!_blowfish_inited)
+//  {
+//    _blowfish_inited = true;
+//    char* key = (char*)"fjghdfjghdfkjg";
+//    _blowfish.Initialize(key, 14);
+//  }
+//}
+
+//void
+//DataImpl :: blowfish_encrypt (char* t, const StringView& s)
+//{
+//
+//  std::cerr<<"bf encrypt "<<s<<"\n";
+//
+//  size_t len (s.len);
+//  std::string str(s);
+//  int i(0);
+//  for (;i<len%8;++i) str += " ";
+//  _blowfish.Encode ((char*)str.c_str(),t,len+i);
+//
+//}
+
+//void
+//DataImpl :: blowfish_decrypt (char* t, size_t len)
+//{
+//
+//  std::cerr<<"bf decrypt "<<t<<" "<<len<<"\n";
+//
+//  std::string str((char*)t);
+//  int i(0);
+//  for (;i<len%8;++i) str += " ";
+//  char* buf = (char*)str.c_str();
+//  _blowfish.Decode (buf,buf,len+i);
+//  t = buf;
+//
+//}
+
diff --git a/pan/data-impl/data-impl.h b/pan/data-impl/data-impl.h
index 1cd97b1..8590fd9 100644
--- a/pan/data-impl/data-impl.h
+++ b/pan/data-impl/data-impl.h
@@ -33,6 +33,7 @@
 #include <pan/general/sorted-vector.h>
 #include <pan/usenet-utils/numbers.h>
 #include <pan/usenet-utils/scorefile.h>
+#include <pan/usenet-utils/blowfish.h>
 #include <pan/data/article.h>
 #include <pan/data/article-cache.h>
 #include <pan/data/encode-cache.h>
@@ -72,6 +73,8 @@ namespace pan
     public ProfilesImpl
   {
 
+    typedef Data::PasswordData PasswordData;
+
     /**
     *** SERVERS
     **/
@@ -97,6 +100,21 @@ namespace pan
       CertStore _certstore;
 
     private:
+//      CBlowFish _blowfish;
+//      bool _blowfish_inited;
+
+    public:
+//      void blowfish_init ();
+//      void blowfish_encrypt (char*, const StringView&);
+//      void blowfish_decrypt (char* t, size_t len);
+
+
+    public:
+#ifdef HAVE_GKR
+      GnomeKeyringResult password_encrypt (const PasswordData&);
+      GnomeKeyringResult password_decrypt (PasswordData&) const;
+#endif
+    private:
 
       void rebuild_backend ();
       const bool _unit_test;
@@ -670,6 +688,7 @@ namespace pan
         save_newsrc_files(*_data_io);
         newsrc_autosave_id = 0;
       }
+
   };
 }
 
diff --git a/pan/data-impl/server.cc b/pan/data-impl/server.cc
index 729c1fa..ff6cb3f 100644
--- a/pan/data-impl/server.cc
+++ b/pan/data-impl/server.cc
@@ -127,7 +127,14 @@ DataImpl :: set_server_auth (const Quark       & server,
   assert (s);
 
   s->username = username;
+#ifndef HAVE_GKR
   s->password = password;
+#else
+  PasswordData pw ;
+  pw.server = s->host;
+  pw.user = username;
+  pw.pw = password;
+#endif
 
 }
 
@@ -194,13 +201,14 @@ DataImpl :: save_server_info (const Quark& server)
 
 }
 
+
 bool
 DataImpl :: get_server_auth (const Quark   & server,
                              std::string   & setme_username,
                              std::string   & setme_password) const
 {
   const Server * s (find_server (server));
-  const bool found (s);
+  bool found (s);
   if (found) {
     setme_username = s->username;
     setme_password = s->password;
@@ -383,7 +391,32 @@ DataImpl :: load_server_properties (const DataIO& source)
     keyvals_t kv (it->second);
     s.host = kv["host"];
     s.username = kv["username"];
+#ifndef HAVE_GKR
     s.password = kv["password"];
+#else
+    PasswordData pw ;
+    pw.server = s.host;
+    pw.user = s.username;
+    GnomeKeyringResult res (password_decrypt(pw));
+    switch (res)
+    {
+      case GNOME_KEYRING_RESULT_NO_MATCH:
+        Log::add_info_va(_("There seems to be no password set for server %s."), s.host.c_str());
+        break;
+
+      case GNOME_KEYRING_RESULT_NO_KEYRING_DAEMON:
+        Log::add_urgent_va (_("The gnome keyring denied access to the passwords."), s.host.c_str());
+        break;
+
+      case GNOME_KEYRING_RESULT_OK:
+        s.password.assign(pw.pw.str, pw.pw.len);
+        break;
+
+      default:
+        Log::add_info_va ("%s %s",gnome_keyring_result_to_message (res),res);
+        break;
+    }
+#endif
     s.port = to_int (kv["port"], STD_NNTP_PORT);
     s.max_connections = to_int (kv["connection-limit"], 2);
     s.article_expiration_age = to_int(kv["expire-articles-n-days-old"], 31);
diff --git a/pan/data/data.h b/pan/data/data.h
index 89bf317..79dd046 100644
--- a/pan/data/data.h
+++ b/pan/data/data.h
@@ -35,6 +35,9 @@
 #include <pan/data/cert-store.h>
 #include <pan/data/server-info.h>
 
+#include <gnome-keyring-1/gnome-keyring.h>
+#include <gnome-keyring-1/gnome-keyring-memory.h>
+
 namespace pan
 {
   class FilterInfo;
@@ -165,6 +168,14 @@ namespace pan
   {
 
     public:
+      struct PasswordData
+      {
+        Quark server;
+        StringView user;
+        StringView pw;
+      };
+
+    public:
       struct Server
       {
          std::string username;
@@ -204,7 +215,10 @@ namespace pan
       virtual const CertStore& get_certstore () const = 0;
 
     public:
-
+#ifdef HAVE_GKR
+      virtual GnomeKeyringResult password_encrypt (const PasswordData&) = 0;
+      virtual GnomeKeyringResult password_decrypt (PasswordData&) const = 0;
+#endif
       /** Gets a quark to the provided hostname */
       virtual bool find_server_by_hn (const Quark& server, Quark& setme) const = 0;
 
diff --git a/pan/gui/Makefile.am b/pan/gui/Makefile.am
index 3db1bed..c5517f8 100644
--- a/pan/gui/Makefile.am
+++ b/pan/gui/Makefile.am
@@ -1,4 +1,5 @@
-AM_CPPFLAGS = -I top_srcdir@ @GTKSPELL_CFLAGS@ @GTK_CFLAGS@ @GMIME_CFLAGS@ @GLIB_CFLAGS@ @OPENSSL_CFLAGS@ @LIBNOTIFY_CFLAGS@ -DPANLOCALEDIR=\""$(panlocaledir)"\"
+AM_CPPFLAGS = -I top_srcdir@ @GTKSPELL_CFLAGS@ @GTK_CFLAGS@ @GMIME_CFLAGS@ @GLIB_CFLAGS@ \
+							@OPENSSL_CFLAGS@ @LIBNOTIFY_CFLAGS@ @LIBGNOME_KEYRING_1_CFLAGS@ -DPANLOCALEDIR=\""$(panlocaledir)"\"
 
 noinst_LIBRARIES = libpangui.a
 
@@ -94,7 +95,7 @@ endif
 
 pan_SOURCES = gui.cc pan.cc $(WINRC)
 pan_LDADD = ./libpangui.a $(WINRCOBJ) ../data-impl/libpandata.a ../tasks/libtasks.a ../data/libdata.a ../usenet-utils/libusenetutils.a ../general/libgeneralutils.a \
-            ../../uulib/libuu.a @GTKSPELL_LIBS@ @GTK_LIBS@ @GMIME_LIBS@ @GLIB_LIBS@ @OPENSSL_LIBS@ @LIBNOTIFY_LIBS@
+            ../../uulib/libuu.a @GTKSPELL_LIBS@ @GTK_LIBS@ @GMIME_LIBS@ @GLIB_LIBS@ @OPENSSL_LIBS@ @LIBNOTIFY_LIBS@ @LIBGNOME_KEYRING_1_LIBS@ 
 
 if HAVE_WIN32
 pan_LDFLAGS = -mwindows
diff --git a/pan/gui/actions.cc b/pan/gui/actions.cc
index cf886c9..e553163 100644
--- a/pan/gui/actions.cc
+++ b/pan/gui/actions.cc
@@ -64,7 +64,8 @@ namespace
     { icon_read_unread_thread, "ICON_READ_UNREAD_THREAD" },
     { icon_score, "ICON_SCORE" },
     { icon_search_pulldown, "ICON_SEARCH_PULLDOWN" },
-    { icon_red_flag, "ICON_FLAGGED"}
+    { icon_red_flag, "ICON_FLAGGED"},
+    { icon_get_flagged, "ICON_GET_FLAGGED" }
   };
 
   void
@@ -149,6 +150,8 @@ namespace
   void do_flag_off                     (GtkAction*) { pan_ui->do_flag_off(); }
   void do_next_flag                    (GtkAction*) { pan_ui->do_next_flag(); }
   void do_last_flag                    (GtkAction*) { pan_ui->do_last_flag(); }
+  void do_download_flagged             (GtkAction*) { pan_ui->do_download_flagged(); }
+  void do_invert_selection             (GtkAction*) { pan_ui->do_invert_selection(); }
   void do_mark_all_flagged             (GtkAction*) { pan_ui->do_mark_all_flagged(); }
   void do_show_score_dialog            (GtkAction*) { pan_ui->do_show_score_dialog(); }
   void do_show_new_score_dialog        (GtkAction*) { pan_ui->do_show_new_score_dialog(); }
@@ -306,6 +309,11 @@ namespace
       N_("Get New Headers in Subscribed Groups"),
       G_CALLBACK(do_xover_subscribed_groups) },
 
+    { "update-stats-in-selected-groups", "ICON_GET_SUBSCRIBED",
+      N_("Get New _Headers in Subscribed Groups"), "<shift>A",
+      N_("Get New Headers in Subscribed Groups"),
+      G_CALLBACK(do_xover_subscribed_groups) },
+
     { "download-headers", "ICON_GET_DIALOG",
       N_("Get _Headers..."), "",
       N_("Get Headers..."),
@@ -569,6 +577,16 @@ namespace
       N_("_Goto last flagged Thread"),
       G_CALLBACK(do_last_flag) },
 
+    { "get-all-flagged", NULL,
+      N_("_Get all flagged Thread"), NULL, ///TODO invent shortcut
+      N_("_Get all flagged Thread"),
+      G_CALLBACK(do_download_flagged) },
+
+    { "invert-selection", NULL,
+      N_("_Invert Selection"), "<control>I",
+      N_("_Invert Selection"),
+      G_CALLBACK(do_invert_selection) },
+
     { "view-article-score", "ICON_SCORE",
       N_("Edit Article's Watch/Ignore/Score..."), "<control><shift>C",
       NULL,
diff --git a/pan/gui/dl-headers-ui.cc b/pan/gui/dl-headers-ui.cc
index 2eaaa3f..1c585a2 100644
--- a/pan/gui/dl-headers-ui.cc
+++ b/pan/gui/dl-headers-ui.cc
@@ -132,7 +132,7 @@ pan :: headers_dialog (Data& data, Prefs& prefs, Queue& queue,
     GtkWidget *w, *x;
     GtkAdjustment * adj;
     int row = 0;
-    state->n_days_rb         = w = gtk_radio_button_new_with_mnemonic (NULL, _("Get the last N _days' headers: "));
+    state->n_days_rb = w = gtk_radio_button_new_with_mnemonic (NULL, _("Get the last N _days' headers: "));
     gtk_button_set_alignment (GTK_BUTTON(w), 0.5, 0.0);
     gtk_table_attach_defaults (GTK_TABLE(t), w, 0, 1, row, row+1);
     adj = GTK_ADJUSTMENT(gtk_adjustment_new (n_days, 1, INT_MAX, 1, 1, 0));
diff --git a/pan/gui/gui.cc b/pan/gui/gui.cc
index feb329f..bb1d060 100644
--- a/pan/gui/gui.cc
+++ b/pan/gui/gui.cc
@@ -1190,6 +1190,21 @@ GUI :: do_last_flag ()
   step_bookmarks(-1);
 }
 
+void
+GUI :: do_download_flagged ()
+{
+  do_mark_all_flagged();
+  do_download_selected_article();
+  do_unselect_all_articles();
+}
+
+void
+GUI :: do_invert_selection ()
+{
+//  std::cerr<<__LINE__<< " "<<__FILE__<<" : implement me.\n";
+  _header_pane->invert_selection();
+}
+
 void GUI :: do_plonk ()
 {
   score_add (ScoreAddDialog::PLONK);
diff --git a/pan/gui/gui.h b/pan/gui/gui.h
index b38a447..1f38559 100644
--- a/pan/gui/gui.h
+++ b/pan/gui/gui.h
@@ -140,6 +140,8 @@ namespace pan
       virtual void do_next_flag ();
       virtual void do_last_flag ();
       virtual void do_mark_all_flagged ();
+      virtual void do_download_flagged ();
+      virtual void do_invert_selection ();
       virtual void do_show_score_dialog ();
       virtual void do_show_new_score_dialog ();
       virtual void do_cancel_article ();
diff --git a/pan/gui/header-pane.cc b/pan/gui/header-pane.cc
index db5e472..4ce33a9 100644
--- a/pan/gui/header-pane.cc
+++ b/pan/gui/header-pane.cc
@@ -90,6 +90,7 @@ namespace
     ICON_ERROR,
     ICON_EMPTY,
     ICON_FLAGGED,
+    ICON_GET_FLAGGED,
     ICON_QTY
    };
 
@@ -110,7 +111,8 @@ namespace
     { icon_bluecheck,              0 },
     { icon_x,                      0 },
     { icon_empty,                  0 },
-    { icon_red_flag,               0 }
+    { icon_red_flag,               0 },
+    { icon_get_flagged,            0 }
   };
 
   int
@@ -843,6 +845,39 @@ HeaderPane :: mark_all_flagged ()
 }
 
 void
+HeaderPane :: invert_selection ()
+{
+
+  GtkTreeIter iter;
+  GtkTreeModel * model(gtk_tree_view_get_model(GTK_TREE_VIEW(_tree_view)));
+  GtkTreeSelection * sel (gtk_tree_view_get_selection (GTK_TREE_VIEW(_tree_view)));
+  gtk_tree_model_get_iter_first (model, &iter);
+  walk_and_invert_selection (model, &iter, sel);
+
+}
+
+
+void
+HeaderPane :: walk_and_invert_selection (GtkTreeModel          * model,
+                                         GtkTreeIter           * cur,
+                                         GtkTreeSelection      * ref) const
+{
+   for (;;) {
+    const bool selected(gtk_tree_selection_iter_is_selected (ref, cur));
+    if (selected)
+      gtk_tree_selection_unselect_iter(ref,cur);
+    else
+      gtk_tree_selection_select_iter(ref,cur);
+    GtkTreeIter child;
+    if (gtk_tree_model_iter_children (model, &child, cur))
+      walk_and_invert_selection (model, &child, ref);
+    if (!gtk_tree_model_iter_next (model, cur))
+      break;
+  }
+
+}
+
+void
 HeaderPane :: walk_and_collect_flagged (GtkTreeModel  * model,
                                 GtkTreeIter           * cur,
                                 GtkTreeSelection      * setme) const
diff --git a/pan/gui/header-pane.h b/pan/gui/header-pane.h
index 8552c53..a29e021 100644
--- a/pan/gui/header-pane.h
+++ b/pan/gui/header-pane.h
@@ -123,6 +123,7 @@ namespace pan
       std::set<const Article*> get_full_selection () const;
       std::vector<const Article*> get_full_selection_v () const;
       void mark_all_flagged ();
+      void invert_selection ();
       const guint get_full_selection_rows_num () const;
       std::set<const Article*> get_nested_selection (bool do_mark_all) const;
       bool set_group (const Quark& group);
@@ -347,7 +348,8 @@ namespace pan
       class RowInserter;
       class SimilarWalk;
       void walk_and_collect (GtkTreeModel*, GtkTreeIter*, articles_set&) const;
-      void walk_and_collect_flagged (GtkTreeModel*, GtkTreeIter*, GtkTreeSelection*) const;
+      void walk_and_collect_flagged  (GtkTreeModel*, GtkTreeIter*, GtkTreeSelection*) const;
+      void walk_and_invert_selection (GtkTreeModel*, GtkTreeIter*, GtkTreeSelection*) const;
 
     private:
       typedef void RenderFunc (GtkTreeViewColumn*, GtkCellRenderer*, GtkTreeModel*, GtkTreeIter*, gpointer);
diff --git a/pan/gui/pan-ui.h b/pan/gui/pan-ui.h
index 96932f7..a32be83 100644
--- a/pan/gui/pan-ui.h
+++ b/pan/gui/pan-ui.h
@@ -71,6 +71,8 @@ namespace pan
     virtual void do_next_flag () = 0;
     virtual void do_last_flag () = 0;
     virtual void do_mark_all_flagged () = 0;
+    virtual void do_download_flagged () = 0;
+    virtual void do_invert_selection () = 0;
     virtual void do_cancel_article () = 0;
     virtual void do_supersede_article () = 0;
     virtual void do_delete_article () = 0;
diff --git a/pan/gui/pan.cc b/pan/gui/pan.cc
index f14e488..53a64ee 100644
--- a/pan/gui/pan.cc
+++ b/pan/gui/pan.cc
@@ -67,6 +67,10 @@ extern "C" {
 #include "server-ui.h"
 #include "pad.h"
 
+#include <gnome-keyring-1/gnome-keyring.h>
+#include <gnome-keyring-1/gnome-keyring-memory.h>
+
+
 //#define DEBUG_LOCALE 1
 
 using namespace pan;
@@ -78,6 +82,8 @@ namespace
 
 namespace
 {
+//  bool keyring_active(gnome_keyring_is_available());
+
   GMainLoop * nongui_gmainloop (0);
 
   void mainloop ()
diff --git a/pan/gui/pan.ui.h b/pan/gui/pan.ui.h
index bbfa8f9..3be86ae 100644
--- a/pan/gui/pan.ui.h
+++ b/pan/gui/pan.ui.h
@@ -120,6 +120,8 @@ const char * fallback_ui_file =
 "        <menuitem action='download-selected-article' />\n"
 "        <menuitem action='show-selected-article-info' />\n"
 "        <separator />\n"
+"        <menuitem action='invert-selection' />\n"
+"        <separator />\n"
 "        <menuitem action='mark-article-read' />\n"
 "        <menuitem action='mark-article-unread' />\n"
 "        <separator />\n"
@@ -136,6 +138,8 @@ const char * fallback_ui_file =
 "        <menuitem action='last-flagged' />\n"
 "        <menuitem action='mark-all-flagged' />\n"
 "        <separator />\n"
+"        <menuitem action='get-all-flagged' />\n"
+"        <separator />\n"
 "        <menuitem action='plonk' />\n"
 "        <menuitem action='view-article-score' />\n"
 "        <separator />\n"
@@ -194,6 +198,8 @@ const char * fallback_ui_file =
 "    <menuitem action='download-selected-article' />\n"
 "    <menuitem action='show-selected-article-info' />\n"
 "    <separator />\n"
+"    <menuitem action='invert-selection' />\n"
+"    <separator />\n"
 "    <menuitem action='mark-article-read' />\n"
 "    <menuitem action='mark-article-unread' />\n"
 "    <separator />\n"
@@ -210,6 +216,8 @@ const char * fallback_ui_file =
 "    <menuitem action='last-flagged' />\n"
 "    <menuitem action='mark-all-flagged' />\n"
 "    <separator />\n"
+"    <menuitem action='get-all-flagged' />\n"
+"    <separator />\n"
 "    <menuitem action='plonk' />\n"
 "    <menuitem action='view-article-score' />\n"
 "    <separator />\n"
diff --git a/pan/gui/server-ui.cc b/pan/gui/server-ui.cc
index 6b06112..35ea2c8 100644
--- a/pan/gui/server-ui.cc
+++ b/pan/gui/server-ui.cc
@@ -183,6 +183,7 @@ namespace
   server_edit_response_cb (GtkDialog * w, int response, gpointer user_data)
   {
     bool destroy (true);
+    bool bf_set (false);
 
     ServerEditDialog * d (static_cast<ServerEditDialog*>(user_data));
     g_assert (d!=NULL);
@@ -195,7 +196,8 @@ namespace
       const int port (gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(d->port_spin)));
       const int max_conn (gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(d->connection_limit_spin)));
       StringView user (pan_entry_get_text (d->auth_username_entry));
-      StringView pass (pan_entry_get_text (d->auth_password_entry));
+      StringView pass = pan_entry_get_text (d->auth_password_entry);
+
       int age (31);
       GtkTreeIter iter;
       GtkComboBox * combo (GTK_COMBO_BOX (d->expiration_age_combo));
@@ -415,8 +417,10 @@ pan :: server_edit_dialog_new (Data& data, Queue& queue, GtkWindow * window, con
     e = gtk_event_box_new ();
     gtk_container_add (GTK_CONTAINER(e), l);
     gtk_misc_set_alignment (GTK_MISC(l), 0.0f, 0.5f);
-    gtk_widget_set_tooltip_text( e, _("You can set the option for using/disabling secure SSL/TLS connections here. If you enable SSL/TLS, your data is encrypted and secure. "
-                                      "It is encouraged to use this option for privacy reasons."));
+    gtk_widget_set_tooltip_text( e,
+          _("You can set the option for using/disabling secure SSL/TLS connections here. "
+            "If you enable SSL/TLS, your data is encrypted and secure. "
+            "It is encouraged to use this option for privacy reasons."));
     HIG::workarea_add_row (t, &row, e, w);
 #endif
 
diff --git a/pan/icons/icon_get_flagged.png b/pan/icons/icon_get_flagged.png
index d215856..1eb3e77 100644
Binary files a/pan/icons/icon_get_flagged.png and b/pan/icons/icon_get_flagged.png differ
diff --git a/pan/tasks/Makefile.am b/pan/tasks/Makefile.am
index 968692d..54e5145 100644
--- a/pan/tasks/Makefile.am
+++ b/pan/tasks/Makefile.am
@@ -12,6 +12,7 @@ libtasks_a_SOURCES = \
   task-groups.cc \
   task-post.cc \
   task-xover.cc \
+  task-xoverinfo.cc \
   task-upload.cc \
   nntp.cc \
   nzb.cc \
@@ -38,6 +39,7 @@ noinst_HEADERS = \
   task-upload.h \
   task-weak-ordering.h \
   task-xover.h \
+  task-xoverinfo.h \
   nntp.h  \
   nzb.h  \
   queue.h  \
diff --git a/pan/tasks/nntp-pool.cc b/pan/tasks/nntp-pool.cc
index bf7ece7..a82b694 100644
--- a/pan/tasks/nntp-pool.cc
+++ b/pan/tasks/nntp-pool.cc
@@ -170,8 +170,9 @@ NNTP_Pool :: on_socket_created (const StringView  & host,
                                 bool                ok,
                                 Socket            * socket)
 {
-
-  debug("on socket created "<<host<<" "<<ok<<" "<<socket);
+  std::string user, pass;
+  _server_info.get_server_auth (_server, user, pass);
+  debug("on socket created "<<host<<" "<<ok<<" "<<socket<<" "<<pass);
   if (!ok)
   {
     delete socket;
diff --git a/pan/tasks/nntp.cc b/pan/tasks/nntp.cc
index b284996..15030c9 100644
--- a/pan/tasks/nntp.cc
+++ b/pan/tasks/nntp.cc
@@ -302,6 +302,15 @@ NNTP :: help (Listener * l)
 }
 
 void
+NNTP :: get_group (Listener * l)
+{
+   _listener = l;
+   _commands.push_back ("GROUP \r\n");
+   write_next_command();
+}
+
+
+void
 NNTP :: xover (const Quark   & group,
                uint64_t        low,
                uint64_t        high,
@@ -309,8 +318,7 @@ NNTP :: xover (const Quark   & group,
 {
    _listener = l;
 
-   if (group != _group)
-      _commands.push_back (build_command ("GROUP %s\r\n", group.c_str()));
+   get_group(l);
 
    _commands.push_back (build_command ("XOVER %"G_GUINT64_FORMAT"-%"G_GUINT64_FORMAT"\r\n", low, high));
 
@@ -318,6 +326,19 @@ NNTP :: xover (const Quark   & group,
 }
 
 void
+NNTP :: xover_count_only (const Quark   & group,
+                          Listener      * l)
+{
+   _listener = l;
+
+   get_group(l);
+
+   _commands.push_back (build_command ("XOVER"));
+
+   write_next_command ();
+}
+
+void
 NNTP :: list_newsgroups (Listener * l)
 {
    _listener = l;
@@ -340,8 +361,7 @@ NNTP :: article (const Quark     & group,
 {
    _listener = l;
 
-   if (group != _group)
-      _commands.push_back (build_command ("GROUP %s\r\n", group.c_str()));
+   get_group(l);
 
    _commands.push_back (build_command ("ARTICLE %"G_GUINT64_FORMAT"\r\n", article_number));
 
@@ -355,8 +375,7 @@ NNTP :: article (const Quark     & group,
 {
    _listener = l;
 
-   if (group != _group)
-      _commands.push_back (build_command ("GROUP %s\r\n", group.c_str()));
+   get_group(l);
 
    _commands.push_back (build_command ("ARTICLE %s\r\n", message_id));
 
@@ -364,6 +383,63 @@ NNTP :: article (const Quark     & group,
 }
 
 void
+NNTP :: get_headers (const Quark     & group,
+                     const char      * message_id,
+                     Listener        * l)
+{
+  _listener = l;
+
+   get_group(l);
+
+   _commands.push_back (build_command ("HEAD %s\r\n", message_id));
+
+   write_next_command ();
+}
+
+void
+NNTP :: get_headers (const Quark     & group,
+                     uint64_t          article_number,
+                     Listener        * l)
+{
+   _listener = l;
+
+   get_group(l);
+
+   _commands.push_back (build_command ("HEAD %"G_GUINT64_FORMAT"\r\n", article_number));
+
+   write_next_command ();
+}
+
+void
+NNTP :: get_body (const Quark     & group,
+                  const char      * message_id,
+                  Listener        * l)
+{
+  _listener = l;
+
+   get_group(l);
+
+   _commands.push_back (build_command ("BODY %s\r\n", message_id));
+
+   write_next_command ();
+}
+
+void
+NNTP :: get_body (const Quark     & group,
+                  uint64_t          article_number,
+                  Listener        * l)
+{
+   _listener = l;
+
+   if (group != _group) get_group(l);
+
+   _commands.push_back (build_command ("BODY %"G_GUINT64_FORMAT"\r\n", article_number));
+
+   write_next_command ();
+}
+
+
+void
 NNTP :: group (const Quark  & group,
                Listener     * l)
 {
diff --git a/pan/tasks/nntp.h b/pan/tasks/nntp.h
index 3c11970..a6bdcdc 100644
--- a/pan/tasks/nntp.h
+++ b/pan/tasks/nntp.h
@@ -157,11 +157,16 @@ namespace pan
        {}
 
        virtual ~NNTP ()
-       {
-       }
+       {}
 
     public:
 
+      /* Internal only */
+      void get_group (Listener * l);
+      void get_headers (const Quark & group, const char * message_id, Listener  * l);
+      void get_headers (const Quark & group, uint64_t article_number, Listener * l);
+      void get_body (const Quark & group, const char * message_id, Listener  * l);
+      void get_body (const Quark & group, uint64_t article_number, Listener * l);
       /**
        * Lists all available commands.
        */
@@ -188,7 +193,18 @@ namespace pan
                              uint64_t             low,
                              uint64_t             high,
                              Listener           * l);
-
+      /**
+       * Executes an XOVER command: "XOVER" to count
+       * the xover numbers internally
+       *
+       * If successful, this will invoke Listener::on_nntp_line()
+       * for each article header line we get back.
+       *
+       * Listener::on_nntp_done() will be called whether the
+       * command is successful or not.
+       */
+      void xover_count_only (const Quark        & group,
+                             Listener           * l);
 
       /**
        * Executes a LIST command: "LIST"
diff --git a/pan/tasks/task-xoverinfo.cc b/pan/tasks/task-xoverinfo.cc
new file mode 100644
index 0000000..5d19e25
--- /dev/null
+++ b/pan/tasks/task-xoverinfo.cc
@@ -0,0 +1,208 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Pan - A Newsreader for Gtk+
+ * Copyright (C) 2002-2006  Charles Kerr <charles rebelbase com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <config.h>
+#include <cassert>
+#include <cerrno>
+extern "C" {
+  #define PROTOTYPES
+  #include <stdio.h>
+  #include <uulib/uudeview.h>
+  #include <glib/gi18n.h>
+  #include <gmime/gmime-utils.h>
+  #include <zlib.h>
+}
+#include <fstream>
+#include <iostream>
+#include <pan/general/debug.h>
+#include <pan/general/file-util.h>
+#include <pan/general/macros.h>
+#include <pan/general/messages.h>
+#include <pan/general/utf8-utils.h>
+#include <pan/data/data.h>
+#include "nntp.h"
+#include "task-xoverinfo.h"
+
+
+using namespace pan;
+
+namespace
+{
+   std::string
+   get_short_name (const StringView& in)
+   {
+    static const StringView moderated ("moderated");
+    static const StringView d ("d");
+
+    StringView myline, long_token;
+
+    // find the long token -- use the last, unless that's "moderated" or "d"
+    myline = in;
+    myline.pop_last_token (long_token, '.');
+    if (!myline.empty() && (long_token==moderated || long_token==d))
+      myline.pop_last_token (long_token, '.');
+
+    // build a new string where each token is shortened except for long_token
+    std::string out;
+    myline = in;
+    StringView tok;
+    while (myline.pop_token (tok, '.')) {
+      out.insert (out.end(), tok.begin(), (tok==long_token ? tok.end() : tok.begin()+1));
+      out += '.';
+    }
+    if (!out.empty())
+      out.erase (out.size()-1);
+
+    return out;
+  }
+
+  std::string get_description (const Quark& group)
+  {
+    char buf[1024];
+    snprintf (buf, sizeof(buf), _("Getting header counts for \"%s\""), group.c_str());
+    return std::string (buf);
+  }
+}
+
+TaskXOverInfo :: TaskXOverInfo (Data         & data,
+                                const Quark  & group,
+                                std::map<Quark,xover_t>& xovers) :
+  Task("XOVER", get_description(group)),
+  _data (data),
+  _group (group),
+  _short_group_name (get_short_name (StringView (group.c_str()))),
+  _xovers(xovers)
+{
+
+  debug ("ctor for " << group);
+
+  // add a ``GROUP'' MiniTask for each server that has this group
+  // initialize the _high lookup table to boundaries
+  const MiniTask group_minitask (MiniTask::GROUP);
+  quarks_t servers;
+  _data.group_get_servers (group, servers);
+
+  foreach_const (quarks_t, servers, it)
+  {
+    if (_data.get_server_limits(*it))
+    {
+      _server_to_minitasks[*it].push_front (group_minitask);
+      std::pair<uint64_t,uint64_t>& p (xovers[*it]);
+      p.first = data.get_xover_high (group, *it);
+    }
+  }
+  init_steps (0);
+
+  update_work ();
+}
+
+TaskXOverInfo :: ~TaskXOverInfo ()
+{}
+
+void
+TaskXOverInfo :: use_nntp (NNTP* nntp)
+{
+  const Quark& server (nntp->_server);
+  debug ("got an nntp from " << nntp->_server);
+
+  nntp->xover_count_only (_group, this);
+}
+
+/***
+****
+***/
+
+namespace
+{
+  unsigned long view_to_ul (const StringView& view)
+  {
+    unsigned long ul = 0ul;
+
+    if (!view.empty()) {
+      errno = 0;
+      ul = strtoul (view.str, 0, 10);
+      if (errno)
+        ul = 0ul;
+    }
+
+    return ul;
+  }
+  uint64_t view_to_ull (const StringView& view)
+  {
+    uint64_t ul = 0ul;
+
+    if (!view.empty()) {
+      errno = 0;
+      ul = g_ascii_strtoull (view.str, 0, 10);
+      if (errno)
+        ul = 0ul;
+    }
+
+    return ul;
+  }
+
+  bool header_is_nonencoded_utf8 (const StringView& in)
+  {
+    const bool is_nonencoded (!in.strstr("=?"));
+    const bool is_utf8 (g_utf8_validate (in.str, in.len, 0));
+    return is_nonencoded && is_utf8;
+  }
+}
+
+void
+TaskXOverInfo :: on_nntp_line         (NNTP               * nntp,
+                                       const StringView   & line)
+{
+  uint64_t new_high(atoi(line.str));
+//  nntp
+}
+
+void
+TaskXOverInfo :: on_nntp_done (NNTP              * nntp,
+                               Health              health,
+                               const StringView  & response UNUSED)
+{
+  update_work (true);
+  check_in (nntp, health);
+}
+
+void
+TaskXOverInfo :: update_work (bool subtract_one_from_nntp_count)
+{
+  int nntp_count (get_nntp_count ());
+  if (subtract_one_from_nntp_count)
+    --nntp_count;
+
+  // find any servers we still need
+  quarks_t servers;
+  foreach_const (server_to_minitasks_t, _server_to_minitasks, it)
+    if (!it->second.empty())
+      servers.insert (it->first);
+
+  //std::cerr << LINE_ID << " servers: " << servers.size() << " nntp: " << nntp_count << std::endl;
+
+  if (!servers.empty())
+    _state.set_need_nntp (servers);
+  else if (nntp_count)
+    _state.set_working ();
+  else {
+    _state.set_completed();
+    set_finished(OK);
+  }
+}
diff --git a/pan/tasks/task-xoverinfo.h b/pan/tasks/task-xoverinfo.h
new file mode 100644
index 0000000..afb134c
--- /dev/null
+++ b/pan/tasks/task-xoverinfo.h
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Pan - A Newsreader for Gtk+
+ * Copyright (C) 2002-2006  Charles Kerr <charles rebelbase com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __TASK_XOVERINFO__H__
+#define __TASK_XOVERINFO__H__
+
+#include <map>
+#include <vector>
+#include <sstream>
+
+#include <pan/data/data.h>
+#include <pan/tasks/task.h>
+#include <pan/tasks/nntp.h>
+#include <fstream>
+#include <iostream>
+
+namespace pan
+{
+  /**
+   * Task for downloading a some or all of a newsgroups' headers
+   * @ingroup tasks
+   */
+  class TaskXOverInfo: public Task, private NNTP::Listener
+  {
+    public: // life cycle
+
+      typedef std::pair<uint64_t,uint64_t> xover_t;
+
+      TaskXOverInfo (Data& data, const Quark& group, std::map<Quark,xover_t>& xovers);
+      virtual ~TaskXOverInfo ();
+
+    public: // task subclass
+      virtual unsigned long get_bytes_remaining () const { return 0ul; }
+
+    protected: // task subclass
+      virtual void use_nntp (NNTP * nntp);
+
+    private: // NNTP::Listener
+      virtual void on_nntp_line (NNTP*, const StringView&);
+      virtual void on_nntp_done (NNTP*, Health, const StringView&);
+      virtual void on_nntp_group (NNTP*, const Quark&, unsigned long, uint64_t, uint64_t);
+
+    private: // implementation - minitasks
+      struct MiniTask {
+        enum Type { GROUP, XOVER };
+        Type _type;
+        uint64_t _low, _high;
+        MiniTask (Type type, uint64_t low=0ul, uint64_t high=0ul):
+          _type(type), _low(low), _high(high) {}
+      };
+      typedef std::deque<MiniTask> MiniTasks_t;
+      typedef std::map<Quark,MiniTasks_t> server_to_minitasks_t;
+      server_to_minitasks_t _server_to_minitasks;
+
+    private: // implementation
+      Data& _data;
+      const Quark _group;
+      std::string _short_group_name;
+      typedef std::map<Quark,uint64_t> server_to_high_t;
+      server_to_high_t _high;
+      void update_work (bool subtract_one_from_nntp_count=false);
+      std::set<Quark> _servers_that_got_xover_minitasks;
+      std::map<NNTP*,uint64_t> _last_xover_number;
+      unsigned long _bytes_so_far;
+      unsigned long _parts_so_far;
+      unsigned long _articles_so_far;
+      unsigned long _total_minitasks;
+      std::map<Quark,xover_t>& _xovers;
+
+
+  };
+}
+
+#endif
diff --git a/pan/usenet-utils/Makefile.am b/pan/usenet-utils/Makefile.am
index df16332..7ee5aed 100644
--- a/pan/usenet-utils/Makefile.am
+++ b/pan/usenet-utils/Makefile.am
@@ -11,7 +11,8 @@ libusenetutils_a_SOURCES = \
  numbers.cc \
  scorefile.cc \
  text-massager.cc \
- url-find.cc
+ url-find.cc \
+ blowfish.cc
 
 noinst_HEADERS = \
  defgroup.h \
@@ -24,7 +25,9 @@ noinst_HEADERS = \
  scorefile.h \
  text-massager.h \
  url-find.h \
- ssl-utils.h
+ ssl-utils.h \
+ blowfish.h \
+ blowfish_cyphers.h
 
 #noinst_PROGRAMS = \
 # gnksa-test \
diff --git a/pan/usenet-utils/blowfish.cc b/pan/usenet-utils/blowfish.cc
new file mode 100644
index 0000000..b1ee060
--- /dev/null
+++ b/pan/usenet-utils/blowfish.cc
@@ -0,0 +1,229 @@
+// blowfish.cpp   C++ class implementation of the BLOWFISH encryption algorithm
+// _THE BLOWFISH ENCRYPTION ALGORITHM_
+// by Bruce Schneier
+// Revised code--3/20/94
+// Converted to C++ class 5/96, Jim Conger
+
+#include "blowfish.h"
+#include "blowfish_cyphers.h"	//! holds the random digit tables
+
+#define S(x,i) (SBoxes[i][x.w.byte##i])
+#define bf_F(x) (((S(x,0) + S(x,1)) ^ S(x,2)) + S(x,3))
+#define ROUND(a,b,n) (a.dword ^= bf_F(b) ^ PArray[n])
+
+
+CBlowFish::CBlowFish ()
+{
+ 	PArray = new DWORD [18] ;
+ 	SBoxes = new DWORD [4][256] ;
+}
+
+CBlowFish::~CBlowFish ()
+{
+	delete PArray ;
+	delete [] SBoxes ;
+}
+
+/** the low level (private) encryption function */
+void CBlowFish::Blowfish_encipher (DWORD *xl, DWORD *xr)
+{
+	union aword  Xl, Xr ;
+
+	Xl.dword = *xl ;
+	Xr.dword = *xr ;
+
+	Xl.dword ^= PArray [0];
+	ROUND (Xr, Xl, 1) ;  ROUND (Xl, Xr, 2) ;
+	ROUND (Xr, Xl, 3) ;  ROUND (Xl, Xr, 4) ;
+	ROUND (Xr, Xl, 5) ;  ROUND (Xl, Xr, 6) ;
+	ROUND (Xr, Xl, 7) ;  ROUND (Xl, Xr, 8) ;
+	ROUND (Xr, Xl, 9) ;  ROUND (Xl, Xr, 10) ;
+	ROUND (Xr, Xl, 11) ; ROUND (Xl, Xr, 12) ;
+	ROUND (Xr, Xl, 13) ; ROUND (Xl, Xr, 14) ;
+	ROUND (Xr, Xl, 15) ; ROUND (Xl, Xr, 16) ;
+	Xr.dword ^= PArray [17] ;
+
+	*xr = Xl.dword ;
+	*xl = Xr.dword ;
+}
+
+/** the low level (private) decryption function */
+void CBlowFish::Blowfish_decipher (DWORD *xl, DWORD *xr)
+{
+   union aword  Xl ;
+   union aword  Xr ;
+
+   Xl.dword = *xl ;
+   Xr.dword = *xr ;
+
+   Xl.dword ^= PArray [17] ;
+   ROUND (Xr, Xl, 16) ;  ROUND (Xl, Xr, 15) ;
+   ROUND (Xr, Xl, 14) ;  ROUND (Xl, Xr, 13) ;
+   ROUND (Xr, Xl, 12) ;  ROUND (Xl, Xr, 11) ;
+   ROUND (Xr, Xl, 10) ;  ROUND (Xl, Xr, 9) ;
+   ROUND (Xr, Xl, 8) ;   ROUND (Xl, Xr, 7) ;
+   ROUND (Xr, Xl, 6) ;   ROUND (Xl, Xr, 5) ;
+   ROUND (Xr, Xl, 4) ;   ROUND (Xl, Xr, 3) ;
+   ROUND (Xr, Xl, 2) ;   ROUND (Xl, Xr, 1) ;
+   Xr.dword ^= PArray[0];
+
+   *xl = Xr.dword;
+   *xr = Xl.dword;
+}
+
+
+/** constructs the enctryption sieve */
+void CBlowFish::Initialize (BYTE key[], int keybytes)
+{
+	int  		i, j ;
+	DWORD  		data, datal, datar ;
+	union aword temp ;
+
+	// first fill arrays from data tables
+	for (i = 0 ; i < 18 ; i++)
+		PArray [i] = bf_P [i] ;
+
+	for (i = 0 ; i < 4 ; i++)
+	{
+	 	for (j = 0 ; j < 256 ; j++)
+	 		SBoxes [i][j] = bf_S [i][j] ;
+	}
+
+
+	j = 0 ;
+	for (i = 0 ; i < NPASS + 2 ; ++i)
+	{
+		temp.dword = 0 ;
+		temp.w.byte0 = key[j];
+		temp.w.byte1 = key[(j+1) % keybytes] ;
+		temp.w.byte2 = key[(j+2) % keybytes] ;
+		temp.w.byte3 = key[(j+3) % keybytes] ;
+		data = temp.dword ;
+		PArray [i] ^= data ;
+		j = (j + 4) % keybytes ;
+	}
+
+	datal = 0 ;
+	datar = 0 ;
+
+	for (i = 0 ; i < NPASS + 2 ; i += 2)
+	{
+		Blowfish_encipher (&datal, &datar) ;
+		PArray [i] = datal ;
+		PArray [i + 1] = datar ;
+	}
+
+	for (i = 0 ; i < 4 ; ++i)
+	{
+		for (j = 0 ; j < 256 ; j += 2)
+		{
+		  Blowfish_encipher (&datal, &datar) ;
+		  SBoxes [i][j] = datal ;
+		  SBoxes [i][j + 1] = datar ;
+		}
+	}
+}
+
+/** get output length, which must be even MOD 8 */
+DWORD CBlowFish::GetOutputLength (DWORD lInputLong)
+{
+	DWORD 	lVal ;
+
+	lVal = lInputLong % 8 ;	// find out if uneven number of bytes at the end
+	if (lVal != 0)
+		return lInputLong + 8 - lVal ;
+	else
+		return lInputLong ;
+}
+
+/**  Encode pIntput into pOutput.  Input length in lSize.  Returned value
+ * is length of output which will be even MOD 8 bytes.  Inputbuffer and
+ * output buffer can be the same, but be sure buffer length is even MOD 8. */
+DWORD CBlowFish::Encode (BYTE * pInput, BYTE * pOutput, DWORD lSize)
+{
+	DWORD 	lCount, lOutSize, lGoodBytes ;
+	BYTE	*pi, *po ;
+	int		i, j ;
+	int		SameDest = (pInput == pOutput ? 1 : 0) ;
+
+	lOutSize = GetOutputLength (lSize) ;
+	for (lCount = 0 ; lCount < lOutSize ; lCount += 8)
+	{
+		if (SameDest)	// if encoded data is being written into input buffer
+		{
+		 	if (lCount < lSize - 7)	// if not dealing with uneven bytes at end
+		 	{
+		 	 	Blowfish_encipher ((DWORD *) pInput,
+		 	 		(DWORD *) (pInput + 4)) ;
+		 	}
+		 	else		// pad end of data with null bytes to complete encryption
+		 	{
+				po = pInput + lSize ;	// point at byte past the end of actual data
+				j = (int) (lOutSize - lSize) ;	// number of bytes to set to null
+				for (i = 0 ; i < j ; i++)
+					*po++ = 0 ;
+		 	 	Blowfish_encipher ((DWORD *) pInput,
+		 	 		(DWORD *) (pInput + 4)) ;
+		 	}
+		 	pInput += 8 ;
+		}
+		else 			// output buffer not equal to input buffer, so must copy
+		{         // input to output buffer prior to encrypting
+		 	if (lCount < lSize - 7)	// if not dealing with uneven bytes at end
+		 	{
+		 		pi = pInput ;
+		 		po = pOutput ;
+		 		for (i = 0 ; i < 8 ; i++)
+          // copy bytes to output
+		 			*po++ = *pi++ ;
+		 	 	Blowfish_encipher ((DWORD *) pOutput,	// now encrypt them
+		 	 		(DWORD *) (pOutput + 4)) ;
+		 	}
+		 	else		// pad end of data with null bytes to complete encryption
+		 	{
+		 		lGoodBytes = lSize - lCount ;	// number of remaining data bytes
+		 		po = pOutput ;
+		 		for (i = 0 ; i < (int) lGoodBytes ; i++)
+		 			*po++ = *pInput++ ;
+		 		for (j = i ; j < 8 ; j++)
+		 			*po++ = 0 ;
+		 	 	Blowfish_encipher ((DWORD *) pOutput,
+		 	 		(DWORD *) (pOutput + 4)) ;
+		 	}
+		 	pInput += 8 ;
+		 	pOutput += 8 ;
+		}
+	}
+	return lOutSize ;
+ }
+
+/** Decode pInput into pOutput.  Input length in lSize.  Input buffer and
+ * output buffer can be the same, but be sure buffer length is even MOD 8. */
+void CBlowFish::Decode (BYTE * pInput, BYTE * pOutput, DWORD lSize)
+{
+	DWORD 	lCount ;
+	BYTE	*pi, *po ;
+	int		i ;
+	int		SameDest = (pInput == pOutput ? 1 : 0) ;
+
+	for (lCount = 0 ; lCount < lSize ; lCount += 8)
+	{
+		if (SameDest)	// if encoded data is being written into input buffer
+		{
+	 	 	Blowfish_decipher ((DWORD *) pInput,
+	 	 		(DWORD *) (pInput + 4)) ;
+		 	pInput += 8 ;
+		}
+		else 			// output buffer not equal to input buffer
+		{         // so copy input to output before decoding
+	 		pi = pInput ;
+	 		po = pOutput ;
+	 		for (i = 0 ; i < 8 ; i++)
+	 			*po++ = *pi++ ;
+	 	 	Blowfish_decipher ((DWORD *) pOutput,
+	 	 		(DWORD *) (pOutput + 4)) ;
+		 	pInput += 8 ;
+		 	pOutput += 8 ;
+		}
+	}
+}
diff --git a/pan/usenet-utils/blowfish.h b/pan/usenet-utils/blowfish.h
new file mode 100644
index 0000000..6e06b39
--- /dev/null
+++ b/pan/usenet-utils/blowfish.h
@@ -0,0 +1,79 @@
+// blowfish.h     interface file for blowfish.cpp
+// _THE BLOWFISH ENCRYPTION ALGORITHM_
+// by Bruce Schneier
+// Revised code--3/20/94
+// Converted to C++ class 5/96, Jim Conger
+
+#define MAXKEYBYTES 	56		// 448 bits max
+#define NPASS           16		// SBox passes
+
+#ifndef DWORD
+  #define DWORD  		unsigned long
+#endif
+#ifndef WORD
+  #define WORD  		unsigned short
+#endif
+#ifndef BYTE
+  #define BYTE  		char
+#endif
+
+class CBlowFish
+{
+private:
+	DWORD 		* PArray ;
+	DWORD		(* SBoxes)[256];
+	void 		Blowfish_encipher (DWORD *xl, DWORD *xr) ;
+	void 		Blowfish_decipher (DWORD *xl, DWORD *xr) ;
+
+public:
+			CBlowFish () ;
+			~CBlowFish () ;
+	void 		Initialize (BYTE key[], int keybytes) ;
+	DWORD		GetOutputLength (DWORD lInputLong) ;
+	DWORD		Encode (BYTE * pInput, BYTE * pOutput, DWORD lSize) ;
+	void		Decode (BYTE * pInput, BYTE * pOutput, DWORD lSize) ;
+
+} ;
+
+// choose a byte order for your hardware
+#define ORDER_DCBA	// chosing Intel in this case
+
+#ifdef ORDER_DCBA  	// DCBA - little endian - intel
+	union aword {
+	  DWORD dword;
+	  BYTE byte [4];
+	  struct {
+	    unsigned int byte3:8;
+	    unsigned int byte2:8;
+	    unsigned int byte1:8;
+	    unsigned int byte0:8;
+	  } w;
+	};
+#endif
+
+#ifdef ORDER_ABCD  	// ABCD - big endian - motorola
+	union aword {
+	  DWORD dword;
+	  BYTE byte [4];
+	  struct {
+	    unsigned int byte0:8;
+	    unsigned int byte1:8;
+	    unsigned int byte2:8;
+	    unsigned int byte3:8;
+	  } w;
+	};
+#endif
+
+#ifdef ORDER_BADC  	// BADC - vax
+	union aword {
+	  DWORD dword;
+	  BYTE byte [4];
+	  struct {
+	    unsigned int byte1:8;
+	    unsigned int byte0:8;
+	    unsigned int byte3:8;
+	    unsigned int byte2:8;
+	  } w;
+};
+#endif
+
diff --git a/pan/usenet-utils/blowfish_cyphers.h b/pan/usenet-utils/blowfish_cyphers.h
new file mode 100644
index 0000000..828c445
--- /dev/null
+++ b/pan/usenet-utils/blowfish_cyphers.h
@@ -0,0 +1,268 @@
+// blowfish_cyphers.h
+// header file containing random number tables
+
+static DWORD bf_P[NPASS + 2] = {
+  0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
+  0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
+  0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
+  0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
+  0x9216d5d9, 0x8979fb1b,
+};
+static DWORD bf_S[4][256] = {
+  0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
+  0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
+  0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
+  0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
+  0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,
+  0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
+  0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
+  0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
+  0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
+  0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
+  0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,
+  0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
+  0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,
+  0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
+  0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
+  0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
+  0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,
+  0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
+  0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
+  0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
+  0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
+  0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
+  0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,
+  0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
+  0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
+  0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
+  0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
+  0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
+  0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,
+  0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
+  0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,
+  0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
+  0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
+  0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
+  0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
+  0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
+  0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
+  0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
+  0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
+  0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
+  0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,
+  0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
+  0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,
+  0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
+  0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
+  0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
+  0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,
+  0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
+  0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
+  0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
+  0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
+  0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
+  0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,
+  0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
+  0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
+  0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
+  0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
+  0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
+  0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,
+  0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
+  0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,
+  0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
+  0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
+  0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,
+  0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,
+  0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
+  0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
+  0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
+  0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,
+  0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
+  0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,
+  0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
+  0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
+  0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
+  0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,
+  0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
+  0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,
+  0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
+  0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
+  0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
+  0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,
+  0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
+  0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,
+  0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
+  0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
+  0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
+  0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,
+  0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
+  0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,
+  0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
+  0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
+  0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
+  0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,
+  0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
+  0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,
+  0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
+  0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
+  0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
+  0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,
+  0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
+  0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,
+  0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
+  0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
+  0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
+  0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
+  0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
+  0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,
+  0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
+  0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
+  0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
+  0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,
+  0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
+  0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,
+  0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
+  0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
+  0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
+  0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,
+  0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
+  0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,
+  0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
+  0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
+  0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
+  0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,
+  0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
+  0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,
+  0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
+  0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
+  0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7,
+  0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,
+  0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
+  0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
+  0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
+  0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,
+  0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
+  0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
+  0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
+  0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
+  0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
+  0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,
+  0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
+  0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,
+  0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
+  0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
+  0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
+  0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
+  0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
+  0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,
+  0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
+  0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
+  0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
+  0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,
+  0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
+  0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,
+  0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
+  0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
+  0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
+  0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
+  0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
+  0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,
+  0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
+  0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
+  0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
+  0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
+  0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
+  0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,
+  0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
+  0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
+  0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
+  0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,
+  0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
+  0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,
+  0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
+  0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
+  0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
+  0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
+  0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
+  0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
+  0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
+  0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
+  0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
+  0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
+  0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
+  0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,
+  0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
+  0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
+  0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
+  0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
+  0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
+  0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,
+  0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
+  0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
+  0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0,
+  0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,
+  0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
+  0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
+  0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
+  0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,
+  0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
+  0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
+  0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
+  0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
+  0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
+  0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,
+  0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
+  0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
+  0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
+  0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
+  0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
+  0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,
+  0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
+  0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,
+  0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
+  0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
+  0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
+  0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,
+  0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
+  0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
+  0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
+  0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
+  0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
+  0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,
+  0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
+  0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,
+  0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
+  0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
+  0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
+  0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,
+  0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
+  0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,
+  0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
+  0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
+  0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
+  0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,
+  0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
+  0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
+  0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
+  0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
+  0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
+  0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,
+  0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
+  0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
+  0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
+  0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
+  0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
+  0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,
+  0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
+  0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
+  0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
+  0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
+  0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
+  0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,
+  0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
+  0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
+  0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
+  0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
+  0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6,
+};
diff --git a/yenclib/crc32.c b/yenclib/crc32.c
new file mode 100644
index 0000000..de276f7
--- /dev/null
+++ b/yenclib/crc32.c
@@ -0,0 +1,614 @@
+// adapted from zlib 1.2.3 by putting it on a piece of wood
+// and banging a few nails throught it
+
+/* crc32.c -- compute the CRC-32 of a data stream
+ * Copyright (C) 1995-2005 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ *
+ * Thanks to Rodney Brown <rbrown64 csc com au> for his contribution of faster
+ * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing
+ * tables for updating the shift register in one step with three exclusive-ors
+ * instead of four steps with four exclusive-ors.  This results in about a
+ * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3.
+ */
+
+#define FAR
+
+/* @(#) $Id$ */
+
+#define local static
+
+/* Find a four-byte integer type for crc32_little() and crc32_big(). */
+#ifndef NOBYFOUR
+#    include <limits.h>
+#    define BYFOUR
+#    if (UINT_MAX == 0xffffffffUL)
+       typedef unsigned int u4;
+#    else
+#      if (ULONG_MAX == 0xffffffffUL)
+         typedef unsigned long u4;
+#      else
+#        if (USHRT_MAX == 0xffffffffUL)
+           typedef unsigned short u4;
+#        else
+#          undef BYFOUR     /* can't find a four-byte integer type! */
+#        endif
+#      endif
+#    endif
+#endif /* !NOBYFOUR */
+
+/* Definitions for doing the crc four data bytes at a time. */
+#ifdef BYFOUR
+#  define REV(w) (((w)>>24)+(((w)>>8)&0xff00)+ \
+                (((w)&0xff00)<<8)+(((w)&0xff)<<24))
+   local unsigned long crc32_little (unsigned long,
+                        const unsigned char FAR *, unsigned);
+   local unsigned long crc32_big (unsigned long,
+                        const unsigned char FAR *, unsigned);
+#  define TBLS 8
+#else
+#  define TBLS 1
+#endif /* BYFOUR */
+
+static const unsigned long FAR crc_table[TBLS][256] =
+{
+  {
+    0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL,
+    0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL,
+    0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL,
+    0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL,
+    0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL,
+    0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL,
+    0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL,
+    0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL,
+    0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL,
+    0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL,
+    0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL,
+    0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL,
+    0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL,
+    0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL,
+    0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL,
+    0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL,
+    0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL,
+    0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL,
+    0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL,
+    0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL,
+    0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL,
+    0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL,
+    0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL,
+    0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL,
+    0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL,
+    0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL,
+    0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL,
+    0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL,
+    0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL,
+    0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL,
+    0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL,
+    0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL,
+    0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL,
+    0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL,
+    0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL,
+    0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL,
+    0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL,
+    0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL,
+    0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL,
+    0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL,
+    0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL,
+    0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL,
+    0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL,
+    0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL,
+    0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL,
+    0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL,
+    0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL,
+    0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL,
+    0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL,
+    0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL,
+    0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL,
+    0x2d02ef8dUL
+#ifdef BYFOUR
+  },
+  {
+    0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL,
+    0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL,
+    0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL,
+    0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL,
+    0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL,
+    0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL,
+    0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL,
+    0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL,
+    0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL,
+    0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL,
+    0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL,
+    0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL,
+    0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL,
+    0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL,
+    0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL,
+    0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL,
+    0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL,
+    0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL,
+    0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL,
+    0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL,
+    0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL,
+    0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL,
+    0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL,
+    0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL,
+    0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL,
+    0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL,
+    0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL,
+    0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL,
+    0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL,
+    0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL,
+    0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL,
+    0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL,
+    0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL,
+    0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL,
+    0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL,
+    0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL,
+    0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL,
+    0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL,
+    0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL,
+    0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL,
+    0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL,
+    0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL,
+    0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL,
+    0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL,
+    0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL,
+    0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL,
+    0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL,
+    0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL,
+    0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL,
+    0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL,
+    0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL,
+    0x9324fd72UL
+  },
+  {
+    0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL,
+    0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL,
+    0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL,
+    0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL,
+    0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL,
+    0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL,
+    0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL,
+    0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL,
+    0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL,
+    0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL,
+    0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL,
+    0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL,
+    0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL,
+    0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL,
+    0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL,
+    0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL,
+    0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL,
+    0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL,
+    0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL,
+    0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL,
+    0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL,
+    0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL,
+    0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL,
+    0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL,
+    0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL,
+    0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL,
+    0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL,
+    0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL,
+    0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL,
+    0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL,
+    0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL,
+    0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL,
+    0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL,
+    0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL,
+    0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL,
+    0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL,
+    0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL,
+    0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL,
+    0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL,
+    0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL,
+    0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL,
+    0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL,
+    0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL,
+    0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL,
+    0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL,
+    0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL,
+    0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL,
+    0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL,
+    0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL,
+    0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL,
+    0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL,
+    0xbe9834edUL
+  },
+  {
+    0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL,
+    0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL,
+    0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL,
+    0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL,
+    0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL,
+    0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL,
+    0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL,
+    0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL,
+    0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL,
+    0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL,
+    0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL,
+    0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL,
+    0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL,
+    0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL,
+    0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL,
+    0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL,
+    0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL,
+    0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL,
+    0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL,
+    0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL,
+    0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL,
+    0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL,
+    0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL,
+    0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL,
+    0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL,
+    0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL,
+    0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL,
+    0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL,
+    0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL,
+    0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL,
+    0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL,
+    0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL,
+    0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL,
+    0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL,
+    0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL,
+    0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL,
+    0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL,
+    0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL,
+    0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL,
+    0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL,
+    0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL,
+    0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL,
+    0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL,
+    0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL,
+    0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL,
+    0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL,
+    0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL,
+    0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL,
+    0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL,
+    0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL,
+    0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL,
+    0xde0506f1UL
+  },
+  {
+    0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL,
+    0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL,
+    0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL,
+    0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL,
+    0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL,
+    0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL,
+    0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL,
+    0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL,
+    0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL,
+    0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL,
+    0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL,
+    0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL,
+    0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL,
+    0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL,
+    0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL,
+    0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL,
+    0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL,
+    0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL,
+    0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL,
+    0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL,
+    0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL,
+    0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL,
+    0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL,
+    0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL,
+    0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL,
+    0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL,
+    0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL,
+    0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL,
+    0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL,
+    0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL,
+    0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL,
+    0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL,
+    0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL,
+    0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL,
+    0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL,
+    0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL,
+    0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL,
+    0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL,
+    0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL,
+    0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL,
+    0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL,
+    0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL,
+    0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL,
+    0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL,
+    0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL,
+    0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL,
+    0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL,
+    0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL,
+    0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL,
+    0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL,
+    0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL,
+    0x8def022dUL
+  },
+  {
+    0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL,
+    0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL,
+    0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL,
+    0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL,
+    0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL,
+    0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL,
+    0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL,
+    0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL,
+    0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL,
+    0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL,
+    0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL,
+    0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL,
+    0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL,
+    0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL,
+    0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL,
+    0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL,
+    0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL,
+    0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL,
+    0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL,
+    0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL,
+    0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL,
+    0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL,
+    0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL,
+    0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL,
+    0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL,
+    0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL,
+    0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL,
+    0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL,
+    0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL,
+    0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL,
+    0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL,
+    0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL,
+    0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL,
+    0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL,
+    0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL,
+    0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL,
+    0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL,
+    0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL,
+    0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL,
+    0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL,
+    0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL,
+    0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL,
+    0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL,
+    0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL,
+    0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL,
+    0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL,
+    0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL,
+    0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL,
+    0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL,
+    0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL,
+    0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL,
+    0x72fd2493UL
+  },
+  {
+    0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL,
+    0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL,
+    0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL,
+    0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL,
+    0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL,
+    0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL,
+    0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL,
+    0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL,
+    0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL,
+    0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL,
+    0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL,
+    0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL,
+    0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL,
+    0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL,
+    0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL,
+    0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL,
+    0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL,
+    0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL,
+    0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL,
+    0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL,
+    0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL,
+    0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL,
+    0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL,
+    0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL,
+    0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL,
+    0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL,
+    0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL,
+    0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL,
+    0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL,
+    0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL,
+    0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL,
+    0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL,
+    0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL,
+    0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL,
+    0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL,
+    0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL,
+    0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL,
+    0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL,
+    0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL,
+    0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL,
+    0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL,
+    0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL,
+    0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL,
+    0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL,
+    0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL,
+    0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL,
+    0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL,
+    0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL,
+    0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL,
+    0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL,
+    0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL,
+    0xed3498beUL
+  },
+  {
+    0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL,
+    0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL,
+    0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL,
+    0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL,
+    0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL,
+    0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL,
+    0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL,
+    0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL,
+    0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL,
+    0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL,
+    0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL,
+    0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL,
+    0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL,
+    0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL,
+    0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL,
+    0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL,
+    0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL,
+    0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL,
+    0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL,
+    0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL,
+    0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL,
+    0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL,
+    0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL,
+    0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL,
+    0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL,
+    0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL,
+    0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL,
+    0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL,
+    0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL,
+    0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL,
+    0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL,
+    0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL,
+    0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL,
+    0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL,
+    0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL,
+    0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL,
+    0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL,
+    0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL,
+    0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL,
+    0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL,
+    0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL,
+    0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL,
+    0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL,
+    0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL,
+    0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL,
+    0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL,
+    0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL,
+    0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL,
+    0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL,
+    0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL,
+    0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL,
+    0xf10605deUL
+#endif
+  }
+};
+
+#include <stddef.h> /* for NULL */
+#include "crc32.h"
+
+/* ========================================================================= */
+#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8)
+#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1
+
+/* ========================================================================= */
+unsigned long crc32(unsigned long crc, const unsigned char * buf, unsigned int len)
+{
+    if (buf == NULL) return 0UL;
+
+#ifdef DYNAMIC_CRC_TABLE
+    if (crc_table_empty)
+        make_crc_table();
+#endif /* DYNAMIC_CRC_TABLE */
+
+#ifdef BYFOUR
+    if (sizeof(void *) == sizeof(ptrdiff_t)) {
+        u4 endian;
+
+        endian = 1;
+        if (*((unsigned char *)(&endian)))
+            return crc32_little(crc, buf, len);
+        else
+            return crc32_big(crc, buf, len);
+    }
+#endif /* BYFOUR */
+    crc = crc ^ 0xffffffffUL;
+    while (len >= 8) {
+        DO8;
+        len -= 8;
+    }
+    if (len) do {
+        DO1;
+    } while (--len);
+    return crc ^ 0xffffffffUL;
+}
+
+#ifdef BYFOUR
+
+/* ========================================================================= */
+#define DOLIT4 c ^= *buf4++; \
+        c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \
+            crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24]
+#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4
+
+/* ========================================================================= */
+local unsigned long crc32_little(crc, buf, len)
+    unsigned long crc;
+    const unsigned char FAR *buf;
+    unsigned len;
+{
+    register u4 c;
+    register const u4 FAR *buf4;
+
+    c = (u4)crc;
+    c = ~c;
+    while (len && ((ptrdiff_t)buf & 3)) {
+        c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8);
+        len--;
+    }
+
+    buf4 = (const u4 FAR *)(const void FAR *)buf;
+    while (len >= 32) {
+        DOLIT32;
+        len -= 32;
+    }
+    while (len >= 4) {
+        DOLIT4;
+        len -= 4;
+    }
+    buf = (const unsigned char FAR *)buf4;
+
+    if (len) do {
+        c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8);
+    } while (--len);
+    c = ~c;
+    return (unsigned long)c;
+}
+
+/* ========================================================================= */
+#define DOBIG4 c ^= *++buf4; \
+        c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \
+            crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24]
+#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4
+
+/* ========================================================================= */
+local unsigned long crc32_big(crc, buf, len)
+    unsigned long crc;
+    const unsigned char FAR *buf;
+    unsigned len;
+{
+    register u4 c;
+    register const u4 FAR *buf4;
+
+    c = REV((u4)crc);
+    c = ~c;
+    while (len && ((ptrdiff_t)buf & 3)) {
+        c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8);
+        len--;
+    }
+
+    buf4 = (const u4 FAR *)(const void FAR *)buf;
+    buf4--;
+    while (len >= 32) {
+        DOBIG32;
+        len -= 32;
+    }
+    while (len >= 4) {
+        DOBIG4;
+        len -= 4;
+    }
+    buf4++;
+    buf = (const unsigned char FAR *)buf4;
+
+    if (len) do {
+        c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8);
+    } while (--len);
+    c = ~c;
+    return (unsigned long)(REV(c));
+}
+
+#endif /* BYFOUR */
diff --git a/yenclib/crc32.h b/yenclib/crc32.h
new file mode 100644
index 0000000..b520cd7
--- /dev/null
+++ b/yenclib/crc32.h
@@ -0,0 +1,38 @@
+#ifndef __CRC32_H__
+#define __CRC32_H__
+
+#ifndef _ANSI_ARGS_
+#ifdef PROTOTYPES
+#define _ANSI_ARGS_(c)	c
+#else
+#define _ANSI_ARGS_(c)	()
+#endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef unsigned long crc32_t;
+#define Z_NULL  0
+
+crc32_t crc32 _ANSI_ARGS_((crc32_t crc, const unsigned char *buf, unsigned int len));
+/*
+     Update a running crc with the bytes buf[0..len-1] and return the updated
+   crc. If buf is NULL, this function returns the required initial value
+   for the crc. Pre- and post-conditioning (one's complement) is performed
+   within this function so it shouldn't be done by the application.
+   Usage example:
+
+     uLong crc = crc32(0L, Z_NULL, 0);
+
+     while (read_buffer(buffer, length) != EOF) {
+       crc = crc32(crc, buffer, length);
+     }
+     if (crc != original_crc) error();
+*/
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/yenclib/uuencode.c b/yenclib/uuencode.c
new file mode 100644
index 0000000..6256ab6
--- /dev/null
+++ b/yenclib/uuencode.c
@@ -0,0 +1,2692 @@
+/*
+ * This file is part of uudeview, the simple and friendly multi-part multi-
+ * file uudecoder  program  (c) 1994-2001 by Frank Pilhofer. The author may
+ * be contacted at fp fpx de
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef SYSTEM_WINDLL
+#include <windows.h>
+#endif
+#ifdef SYSTEM_OS2
+#include <os2.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <time.h>
+#include <math.h>
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include <uudeview.h>
+#include <uuint.h>
+#include <fptools.h>
+#include <uustring.h>
+#include <crc32.h>
+
+/* for braindead systems */
+#ifndef SEEK_SET
+#ifdef L_BEGIN
+#define SEEK_SET L_BEGIN
+#else
+#define SEEK_SET 0
+#endif
+#endif
+
+char * uuencode_id = "$Id: uuencode.c,v 1.22 2002/04/02 10:04:52 fp Exp $";
+
+#if 0
+/*
+ * the End-Of-Line string. MIME enforces CRLF, so that's what we use. Some
+ * implementations of uudecode will complain about a missing end line, since
+ * they look for "end^J" but find "end^J^M". We don't care - especially be-
+ * cause they still decode the file properly despite this complaint.
+ */
+
+#ifndef EOLSTRING
+#define EOLSTRING	"\015\012"
+#endif
+
+#else
+
+/*
+ * Argh. Some delivery software (inews) has problems with the CRLF
+ * line termination. Let's try native EOL and see if we run into
+ * any problems.
+ * This involves opening output files in text mode instead of binary
+ */
+
+#ifndef EOLSTRING
+#define EOLSTRING	"\n"
+#endif
+
+#endif
+
+
+/*
+ * =========================================================================
+ * User-configurable settings end here. Don't spy below unless you know what
+ * you're doing.
+ * =========================================================================
+ */
+
+/*
+ * Define End-Of-Line sequence
+ */
+
+#ifdef EOLSTRING
+static unsigned char *eolstring = (unsigned char *) EOLSTRING;
+#else
+static unsigned char *eolstring = (unsigned char *) "\012";
+#endif
+
+/*
+ * Content-Transfer-Encoding types for non-MIME encodings
+ */
+
+#define CTE_UUENC	"x-uuencode"
+#define CTE_XXENC	"x-xxencode"
+#define CTE_BINHEX	"x-binhex"
+#define CTE_YENC	"x-yenc"
+
+#define CTE_TYPE(y)	(((y)==B64ENCODED) ? "Base64"  : \
+			 ((y)==UU_ENCODED) ? CTE_UUENC : \
+			 ((y)==XX_ENCODED) ? CTE_XXENC : \
+                         ((y)==PT_ENCODED) ? "8bit" : \
+                         ((y)==QP_ENCODED) ? "quoted-printable" : \
+			 ((y)==BH_ENCODED) ? CTE_BINHEX : \
+			 ((y)==YENC_ENCODED) ? CTE_YENC : "x-oops")
+
+/*
+ * encoding tables
+ */
+
+unsigned char UUEncodeTable[64] =
+{
+  '`', '!', '"', '#', '$', '%', '&', '\'',
+  '(', ')', '*', '+', ',', '-', '.', '/',
+  '0', '1', '2', '3', '4', '5', '6', '7',
+  '8', '9', ':', ';', '<', '=', '>', '?',
+  '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
+  'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+  'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
+  'X', 'Y', 'Z', '[', '\\',']', '^', '_'
+};
+
+
+unsigned char B64EncodeTable[64] =
+{
+  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+  'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+  'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+  'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+  'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+  'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+  'w', 'x', 'y', 'z', '0', '1', '2', '3',
+  '4', '5', '6', '7', '8', '9', '+', '/'
+};
+
+unsigned char XXEncodeTable[64] =
+{
+  '+', '-', '0', '1', '2', '3', '4', '5',
+  '6', '7', '8', '9', 'A', 'B', 'C', 'D',
+  'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+  'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
+  'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b',
+  'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
+  'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
+  's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
+};
+
+unsigned char BHEncodeTable[64] =
+{
+  '!', '"', '#', '$', '%', '&', '\'', '(',
+  ')', '*', '+', ',', '-', '0', '1', '2',
+  '3', '4', '5', '6', '8', '9', '@', 'A',
+  'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
+  'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R',
+  'S', 'T', 'U', 'V', 'X', 'Y', 'Z', '[',
+  '`', 'a', 'b', 'c', 'd', 'e', 'f', 'h',
+  'i', 'j', 'k', 'l', 'm', 'p', 'q', 'r'
+};
+
+unsigned char HexEncodeTable[16] =
+{
+  '0', '1', '2', '3', '4', '5', '6', '7',
+  '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+};
+
+typedef struct
+{
+  char *extension;
+  char *mimetype;
+} mimemap;
+
+/*
+ * This table maps a file's extension into a Content-Type. The current
+ * official list can be downloaded as
+ *   ftp://ftp.isi.edu/in-notes/iana/assignments/media-type
+ * I haven't listed any text types since it doesn't make sense to encode
+ * them. Everything not on the list gets mapped to application/octet-stream
+ */
+
+static mimemap mimetable[] =
+{
+  { "gif",  "image/gif"        }, /* Grafics Interchange Format  */
+  { "jpg",  "image/jpeg"       }, /* JFIF encoded files          */
+  { "jpeg", "image/jpeg"       },
+  { "tif",  "image/tiff"       }, /* Tag Image File Format       */
+  { "tiff", "image/tiff"       },
+  { "cgm",  "image/cgm"        }, /* Computer Graphics Metafile  */
+  { "au",   "audio/basic"      }, /* 8kHz ulaw audio data        */
+  { "mov",  "video/quicktime"  }, /* Apple Quicktime             */
+  { "qt",   "video/quicktime"  }, /* Also infrequently used      */
+  { "mpeg", "video/mpeg"       }, /* Motion Picture Expert Group */
+  { "mpg",  "video/mpeg"       },
+  { "mp2",  "video/mpeg"       }, /* dito, MPEG-2 encoded files  */
+  { "mp3",  "audio/mpeg"       }, /* dito, MPEG-3 encoded files  */
+  { "ps",   "application/postscript" }, /* Postscript Language   */
+  { "zip",  "application/zip"  }, /* ZIP archive                 */
+  { "doc",  "application/msword"},/* assume Microsoft Word       */
+  { NULL,   NULL               }
+};
+
+/*
+ * the order of the following two tables must match the
+ * Encoding Types definition in uudeview.h
+ */
+
+/*
+ * encoded bytes per line
+ */
+
+static int bpl[8] = { 0, 45, 57, 45, 45, 0, 0, 128 };
+
+/*
+ * tables
+ */
+
+static unsigned char *etables[5] =
+{
+  NULL,
+  UUEncodeTable,
+  B64EncodeTable,
+  XXEncodeTable,
+  BHEncodeTable
+};
+
+/*
+ * variables to malloc upon initialization
+ */
+
+char *uuestr_itemp;
+char *uuestr_otemp;
+
+/*
+ * Encode one part of the data stream
+ */
+
+static int
+UUEncodeStream (FILE *outfile, FILE *infile, int encoding, long linperfile, crc32_t *crc, crc32_t *pcrc)
+{
+  unsigned char *itemp = (char *) uuestr_itemp;
+  unsigned char *otemp = (char *) uuestr_otemp;
+  unsigned char *optr, *table, *tptr;
+  int index, count;
+  long line=0;
+  size_t llen;
+
+  if (outfile==NULL || infile==NULL ||
+      (encoding!=UU_ENCODED&&encoding!=XX_ENCODED&&encoding!=B64ENCODED&&
+       encoding!=PT_ENCODED&&encoding!=QP_ENCODED&&encoding!=YENC_ENCODED))
+    {
+      UUMessage (uuencode_id, __LINE__, UUMSG_ERROR,
+                 uustring (S_PARM_CHECK), "UUEncodeStream()");
+      return UURET_ILLVAL;
+    }
+
+  /*
+   * Special handling for plain text and quoted printable. Text is
+   * read line oriented.
+   */
+
+  if (encoding == PT_ENCODED || encoding == QP_ENCODED)
+    {
+      while (!feof (infile) && (linperfile <= 0 || line < linperfile))
+        {
+          if (_FP_fgets (itemp, 255, infile) == NULL)
+            {
+              break;
+            }
+
+          itemp[255] = '\0';
+          count = strlen (itemp);
+
+          llen = 0;
+          optr = otemp;
+
+          /*
+           * Busy Callback
+           */
+
+          if (UUBUSYPOLL(ftell(infile)-progress.foffset,progress.fsize))
+            {
+              UUMessage (uuencode_id, __LINE__, UUMSG_NOTE,
+                         uustring (S_ENCODE_CANCEL));
+              return UURET_CANCEL;
+            }
+
+          if (encoding == PT_ENCODED)
+            {
+              /*
+               * If there is a line feed, replace by eolstring
+               */
+              if (count > 0 && itemp[count-1] == '\n')
+                {
+                  itemp[--count] = '\0';
+                  if (fwrite (itemp, 1, count, outfile) != count ||
+                      fwrite ((char *) eolstring, 1,
+                              strlen(eolstring), outfile) != strlen (eolstring))
+                    {
+                      return UURET_IOERR;
+                    }
+                }
+              else
+                {
+                  if (fwrite (itemp, 1, count, outfile) != llen)
+                    {
+                      return UURET_IOERR;
+                    }
+                }
+            }
+          else if (encoding == QP_ENCODED)
+            {
+              for (index=0; index<count; index++)
+                {
+                  if (llen == 0 && itemp[index] == '.')
+                    {
+                      /*
+                       * Special rule: encode '.' at the beginning of a line, so
+                       * that some mailers aren't confused.
+                       */
+                      *optr++ = '=';
+                      *optr++ = HexEncodeTable[itemp[index] >> 4];
+                      *optr++ = HexEncodeTable[itemp[index] & 0x0f];
+                      llen += 3;
+                    }
+                  else if ((itemp[index] >= 33 && itemp[index] <= 60) ||
+                           (itemp[index] >= 62 && itemp[index] <= 126) ||
+                           itemp[index] == 9 || itemp[index] == 32)
+                    {
+                      *optr++ = itemp[index];
+                      llen++;
+                    }
+                  else if (itemp[index] == '\n')
+                    {
+                      /*
+                       * If the last character before EOL was a space or tab,
+                       * we must encode it. If llen > 74, there's no space to do
+                       * that, so generate a soft line break instead.
+                       */
+
+                      if (index>0 && (itemp[index-1] == 9 || itemp[index-1] == 32))
+                        {
+                          *(optr-1) = '=';
+                          if (llen <= 74)
+                            {
+                              *optr++ = HexEncodeTable[itemp[index-1] >> 4];
+                              *optr++ = HexEncodeTable[itemp[index-1] & 0x0f];
+                              llen += 2;
+                            }
+                        }
+
+                      if (fwrite (otemp, 1, llen, outfile) != llen ||
+                          fwrite ((char *) eolstring, 1,
+                                  strlen(eolstring), outfile) != strlen (eolstring))
+                        {
+                          return UURET_IOERR;
+                        }
+
+                      /*
+                       * Fix the soft line break condition from above
+                       */
+
+                      if (index>0 && (itemp[index-1] == 9 || itemp[index-1] == 32) &&
+                          *(optr-1) == '=')
+                        {
+                          otemp[0] = '=';
+                          otemp[1] = HexEncodeTable[itemp[index-1] >> 4];
+                          otemp[2] = HexEncodeTable[itemp[index-1] & 0x0f];
+
+                          if (fwrite (otemp, 1, 3, outfile) != 3 ||
+                              fwrite ((char *) eolstring, 1,
+                                      strlen(eolstring), outfile) != strlen (eolstring))
+                            {
+                              return UURET_IOERR;
+                            }
+                        }
+
+                      optr = otemp;
+                      llen = 0;
+                    }
+                  else
+                    {
+                      *optr++ = '=';
+                      *optr++ = HexEncodeTable[itemp[index] >> 4];
+                      *optr++ = HexEncodeTable[itemp[index] & 0x0f];
+                      llen += 3;
+                    }
+
+                  /*
+                   * Lines must be shorter than 76 characters (not counting CRLF).
+                   * If the line grows longer than that, we must include a soft
+                   * line break.
+                   */
+
+                  if (itemp[index+1] != 0 && itemp[index+1] != '\n' &&
+                      (llen >= 75 ||
+                       (!((itemp[index+1] >= 33 && itemp[index+1] <= 60) ||
+                          (itemp[index+1] >= 62 && itemp[index+1] <= 126)) &&
+                        llen >= 73)))
+                    {
+
+                      *optr++ = '=';
+                      llen++;
+
+                      if (fwrite (otemp, 1, llen, outfile) != llen ||
+                          fwrite ((char *) eolstring, 1,
+                                  strlen(eolstring), outfile) != strlen (eolstring))
+                        {
+                          return UURET_IOERR;
+                        }
+
+                      optr = otemp;
+                      llen = 0;
+                    }
+                }
+            }
+
+          line++;
+        }
+
+      return UURET_OK;
+    }
+
+  /*
+   * Special handling for yEnc
+   */
+
+  if (encoding == YENC_ENCODED)
+    {
+      llen = 0;
+      optr = otemp;
+
+      while (!feof (infile) && (linperfile <= 0 || line < linperfile))
+        {
+          if ((count = fread (itemp, 1, 128, infile)) != 128)
+            {
+              if (count == 0)
+                {
+                  break;
+                }
+              else if (ferror (infile))
+                {
+                  return UURET_IOERR;
+                }
+            }
+
+          if (pcrc)
+            *pcrc = crc32(*pcrc, itemp, count);
+          if (crc)
+            *crc = crc32(*crc, itemp, count);
+
+          line++;
+
+          /*
+           * Busy Callback
+           */
+
+          if (UUBUSYPOLL(ftell(infile)-progress.foffset,progress.fsize))
+            {
+              UUMessage (uuencode_id, __LINE__, UUMSG_NOTE,
+                         uustring (S_ENCODE_CANCEL));
+              return UURET_CANCEL;
+            }
+
+          for (index=0; index<count; index++)
+            {
+              if (llen > 127)
+                {
+                  if (fwrite (otemp, 1, llen, outfile) != llen ||
+                      fwrite ((char *) eolstring, 1,
+                              strlen(eolstring), outfile) != strlen (eolstring))
+                    {
+                      return UURET_IOERR;
+                    }
+                  llen = 0;
+                  optr = otemp;
+                }
+
+              switch ((char) ((int) itemp[index] + 42))
+                {
+                case '\0':
+//                case '\t':  yEnc 1.3 draft
+                case '\n':
+                case '\r':
+                case '=':
+                case '\033':
+                  *optr++ = '=';
+                  *optr++ = (char) ((int) itemp[index] + 42 + 64);
+                  llen += 2;
+                  break;
+
+                case '.':
+                  if (llen == 0)
+                    {
+                      *optr++ = '=';
+                      *optr++ = (char) ((int) itemp[index] + 42 + 64);
+                      llen += 2;
+                    }
+                  else
+                    {
+                      *optr++ = (char) ((int) itemp[index] + 42);
+                      llen++;
+                    }
+                  break;
+
+                default:
+                  *optr++ = (char) ((int) itemp[index] + 42);
+                  llen++;
+                  break;
+                }
+            }
+        }
+
+      /*
+       * write last line
+       */
+
+      if (llen)
+        {
+          if (fwrite (otemp, 1, llen, outfile) != llen ||
+              fwrite ((char *) eolstring, 1,
+                      strlen(eolstring), outfile) != strlen (eolstring))
+            {
+              return UURET_IOERR;
+            }
+        }
+
+      return UURET_OK;
+    }
+
+  /*
+   * Handling for binary encodings
+   */
+
+  /*
+   * select charset
+   */
+
+  table = etables[encoding];
+
+  if (table==NULL || bpl[encoding]==0)
+    {
+      UUMessage (uuencode_id, __LINE__, UUMSG_ERROR,
+                 uustring (S_PARM_CHECK), "UUEncodeStream()");
+      return UURET_ILLVAL;
+    }
+
+  while (!feof (infile) && (linperfile <= 0 || line < linperfile))
+    {
+      if ((count = fread (itemp, 1, bpl[encoding], infile)) != bpl[encoding])
+        {
+          if (count == 0)
+            break;
+          else if (ferror (infile))
+            return UURET_IOERR;
+        }
+
+      optr = otemp;
+      llen = 0;
+
+      /*
+       * Busy Callback
+       */
+
+      if (UUBUSYPOLL(ftell(infile)-progress.foffset,progress.fsize))
+        {
+          UUMessage (uuencode_id, __LINE__, UUMSG_NOTE,
+                     uustring (S_ENCODE_CANCEL));
+          return UURET_CANCEL;
+        }
+
+      /*
+       * for UU and XX, encode the number of bytes as first character
+       */
+
+      if (encoding == UU_ENCODED || encoding == XX_ENCODED)
+        {
+          *optr++ = table[count];
+          llen++;
+        }
+
+      /*
+       * Main encoding
+       */
+
+      for (index=0; index<=count-3; index+=3, llen+=4)
+        {
+          *optr++ = table[itemp[index] >> 2];
+          *optr++ = table[((itemp[index  ] & 0x03) << 4)|(itemp[index+1] >> 4)];
+          *optr++ = table[((itemp[index+1] & 0x0f) << 2)|(itemp[index+2] >> 6)];
+          *optr++ = table[  itemp[index+2] & 0x3f];
+        }
+
+      /*
+       * Special handling for incomplete lines
+       */
+
+      if (index != count)
+        {
+          if (encoding == B64ENCODED)
+            {
+              if (count - index == 2)
+                {
+                  *optr++ = table[itemp[index] >> 2];
+                  *optr++ = table[((itemp[index  ] & 0x03) << 4) |
+                                  ((itemp[index+1] & 0xf0) >> 4)];
+                  *optr++ = table[((itemp[index+1] & 0x0f) << 2)];
+                  *optr++ = '=';
+                }
+              else if (count - index == 1)
+                {
+                  *optr++ = table[ itemp[index] >> 2];
+                  *optr++ = table[(itemp[index] & 0x03) << 4];
+                  *optr++ = '=';
+                  *optr++ = '=';
+                }
+              llen += 4;
+            }
+          else if (encoding == UU_ENCODED || encoding == XX_ENCODED)
+            {
+              if (count - index == 2)
+                {
+                  *optr++ = table[itemp[index] >> 2];
+                  *optr++ = table[((itemp[index  ] & 0x03) << 4) |
+                                  ( itemp[index+1] >> 4)];
+                  *optr++ = table[((itemp[index+1] & 0x0f) << 2)];
+                  *optr++ = table[0];
+                }
+              else if (count - index == 1)
+                {
+                  *optr++ = table[ itemp[index] >> 2];
+                  *optr++ = table[(itemp[index] & 0x03) << 4];
+                  *optr++ = table[0];
+                  *optr++ = table[0];
+                }
+              llen += 4;
+            }
+        }
+
+      /*
+       * end of line
+       */
+
+      tptr = eolstring;
+
+      while (*tptr)
+        *optr++ = *tptr++;
+
+      *optr++ = '\0';
+      llen   += strlen ((char *) eolstring);
+
+      if (fwrite (otemp, 1, llen, outfile) != llen)
+        return UURET_IOERR;
+
+      line++;
+    }
+  return UURET_OK;
+}
+
+static int
+UUEncodeStream_byFSize (FILE *outfile, FILE *infile, int encoding, long bpf, crc32_t *crc, crc32_t *pcrc)
+{
+  unsigned char *itemp = (char *) uuestr_itemp;
+  unsigned char *otemp = (char *) uuestr_otemp;
+  unsigned char *optr, *table, *tptr;
+  int index, count;
+  long offset=0;
+  long line = 0;
+  size_t llen;
+
+  if (outfile==NULL || infile==NULL ||
+      (encoding!=UU_ENCODED&&encoding!=XX_ENCODED&&encoding!=B64ENCODED&&
+       encoding!=PT_ENCODED&&encoding!=QP_ENCODED&&encoding!=YENC_ENCODED))
+    {
+      return UURET_ILLVAL;
+    }
+
+  /*
+   * Special handling for plain text and quoted printable. Text is
+   * read line oriented.
+   */
+
+  long rest = bpf;
+  long current = 0;
+
+  ///FIXME
+
+  if (encoding == PT_ENCODED || encoding == QP_ENCODED)
+    {
+      while (!feof (infile) && rest > 0 )
+      {
+        current = rest > 255 ? 255 : rest;
+
+      if ((count = fread (itemp, sizeof(unsigned char), current, infile)) != 255)
+      {
+        if (count == 0)
+          break;
+        else if (ferror (infile))
+          return UURET_IOERR;
+      }
+
+      rest -= count;
+      itemp[count] = '\0';
+
+      llen = 0;
+      optr = otemp;
+
+        /*
+         * Busy Callback
+         */
+
+      if (UUBUSYPOLL(ftell(infile)-progress.foffset,progress.fsize))
+          return UURET_CANCEL;
+
+      if (encoding == PT_ENCODED)
+      {
+        size_t res;
+        if ((res=fwrite (itemp, sizeof(unsigned char), count, outfile)) != count)
+            return UURET_IOERR;
+      }
+          else if (encoding == QP_ENCODED)
+          {
+            for (index=0; index<count; index++)
+              {
+                if (llen == 0 && itemp[index] == '.')
+                  {
+                    /*
+                     * Special rule: encode '.' at the beginning of a line, so
+                     * that some mailers aren't confused.
+                     */
+                    *optr++ = '=';
+                    *optr++ = HexEncodeTable[itemp[index] >> 4];
+                    *optr++ = HexEncodeTable[itemp[index] & 0x0f];
+                    llen += 3;
+                  }
+                else if ((itemp[index] >= 33 && itemp[index] <= 60) ||
+                         (itemp[index] >= 62 && itemp[index] <= 126) ||
+                         itemp[index] == 9 || itemp[index] == 32)
+                  {
+                    *optr++ = itemp[index];
+                    llen++;
+                  }
+                else if (itemp[index] == '\n')
+                  {
+                    /*
+                     * If the last character before EOL was a space or tab,
+                     * we must encode it. If llen > 74, there's no space to do
+                     * that, so generate a soft line break instead.
+                     */
+
+                    if (index>0 && (itemp[index-1] == 9 || itemp[index-1] == 32))
+                      {
+                        *(optr-1) = '=';
+                        if (llen <= 74)
+                          {
+                            *optr++ = HexEncodeTable[itemp[index-1] >> 4];
+                            *optr++ = HexEncodeTable[itemp[index-1] & 0x0f];
+                            llen += 2;
+                          }
+                      }
+
+                    if (fwrite (otemp, 1, llen, outfile) != llen ||
+                        fwrite ((char *) eolstring, 1,
+                                strlen(eolstring), outfile) != strlen (eolstring))
+                      {
+                        return UURET_IOERR;
+                      }
+
+                    /*
+                     * Fix the soft line break condition from above
+                     */
+
+                    if (index>0 && (itemp[index-1] == 9 || itemp[index-1] == 32) &&
+                        *(optr-1) == '=')
+                      {
+                        otemp[0] = '=';
+                        otemp[1] = HexEncodeTable[itemp[index-1] >> 4];
+                        otemp[2] = HexEncodeTable[itemp[index-1] & 0x0f];
+
+                        if (fwrite (otemp, 1, 3, outfile) != 3 ||
+                            fwrite ((char *) eolstring, 1,
+                                    strlen(eolstring), outfile) != strlen (eolstring))
+                          {
+                            return UURET_IOERR;
+                          }
+                      }
+
+                    optr = otemp;
+                    llen = 0;
+                  }
+                else
+                  {
+                    *optr++ = '=';
+                    *optr++ = HexEncodeTable[itemp[index] >> 4];
+                    *optr++ = HexEncodeTable[itemp[index] & 0x0f];
+                    llen += 3;
+                  }
+
+                /*
+                 * Lines must be shorter than 76 characters (not counting CRLF).
+                 * If the line grows longer than that, we must include a soft
+                 * line break.
+                 */
+
+                if (itemp[index+1] != 0 && itemp[index+1] != '\n' &&
+                    (llen >= 75 ||
+                     (!((itemp[index+1] >= 33 && itemp[index+1] <= 60) ||
+                        (itemp[index+1] >= 62 && itemp[index+1] <= 126)) &&
+                      llen >= 73)))
+                  {
+
+                    *optr++ = '=';
+                    llen++;
+
+                    if (fwrite (otemp, 1, llen, outfile) != llen ||
+                        fwrite ((char *) eolstring, 1,
+                                strlen(eolstring), outfile) != strlen (eolstring))
+                      {
+                        return UURET_IOERR;
+                      }
+
+                    optr = otemp;
+                    llen = 0;
+                  }
+              }
+          }
+        line++;
+      }
+
+      return UURET_OK;
+    }
+
+  /*
+   * Special handling for yEnc
+   */
+
+  if (encoding == YENC_ENCODED)
+  {
+    llen = 0;
+    optr = otemp;
+    long rest = bpf;
+    long current = 0;
+
+    while (rest > 0)
+    {
+      current = rest > 128 ? 128 : rest;
+
+      if ((count = fread (itemp, sizeof(unsigned char), current, infile)) != 128)
+      {
+        if (count == 0)
+        {
+          break;
+        }
+        else if (ferror (infile))
+        {
+          return UURET_IOERR;
+        }
+
+      }
+
+      if (pcrc)
+        *pcrc = crc32(*pcrc, itemp, count);
+      if (crc)
+        *crc = crc32(*crc, itemp, count);
+
+      line++;
+
+      /*
+       * Busy Callback
+       */
+
+      if (UUBUSYPOLL(ftell(infile)-progress.foffset,progress.fsize))
+        {
+          UUMessage (uuencode_id, __LINE__, UUMSG_NOTE,
+                     uustring (S_ENCODE_CANCEL));
+          return UURET_CANCEL;
+        }
+
+      for (index=0; index<count; index++)
+        {
+          if (llen > 127 )
+            {
+              if (fwrite (otemp, sizeof(unsigned char), llen, outfile) != llen ||
+                  fwrite ((char *) eolstring, sizeof(unsigned char),
+                          strlen(eolstring), outfile) != strlen (eolstring))
+                {
+                  return UURET_IOERR;
+                }
+              llen = 0;
+              optr = otemp;
+            }
+
+          char tmp = (char) ((int) itemp[index] + 42);
+
+          switch (tmp)
+            {
+            case '\0':
+//          case '\t':  yEnc 1.3 draft
+            case '\n':
+            case '\r':
+            case '=':
+            case '\033':
+              *optr++ = '=';
+              *optr++ = tmp + 64;
+              llen += 2;
+              break;
+
+            case '.':
+              if (llen == 0)
+                {
+                  *optr++ = '=';
+                  *optr++ =  tmp + 64;
+                  llen += 2;
+                  break;
+              }
+
+            default:
+              *optr++ = tmp;
+              llen++;
+              break;
+            }
+        }
+
+        rest -= count;
+    }
+
+
+    /*
+     * write last line
+     */
+
+    if (llen)
+    {
+      if (fwrite (otemp, 1, llen, outfile) != llen ||
+          fwrite ((char *) eolstring, 1,
+                  strlen(eolstring), outfile) != strlen (eolstring))
+        {
+          return UURET_IOERR;
+        }
+    }
+
+    return UURET_OK;
+  }
+
+  /*
+   * Handling for binary encodings
+   */
+
+  /*
+   * select charset
+   */
+
+  table = etables[encoding];
+
+  if (table==NULL || bpl[encoding]==0)
+  {
+    return UURET_ILLVAL;
+  }
+
+  while (!feof (infile) && rest > 0)
+  {
+    current = rest >  bpl[encoding] ?  bpl[encoding] : rest;
+    if ((count = fread (itemp, 1, current, infile)) != bpl[encoding])
+    {
+      if (count == 0)
+        break;
+      else if (ferror (infile))
+        return UURET_IOERR;
+    }
+
+    rest -= count;
+
+    optr = otemp;
+    llen = 0;
+
+    /*
+     * Busy Callback
+     */
+
+    if (UUBUSYPOLL(ftell(infile)-progress.foffset,progress.fsize))
+      {
+        UUMessage (uuencode_id, __LINE__, UUMSG_NOTE,
+                   uustring (S_ENCODE_CANCEL));
+        return UURET_CANCEL;
+      }
+
+    /*
+     * for UU and XX, encode the number of bytes as first character
+     */
+
+    if (encoding == UU_ENCODED || encoding == XX_ENCODED)
+      {
+        *optr++ = table[count];
+        llen++;
+      }
+
+    /*
+     * Main encoding
+     */
+
+    for (index=0; index<=count-3; index+=3, llen+=4)
+      {
+        *optr++ = table[itemp[index] >> 2];
+        *optr++ = table[((itemp[index  ] & 0x03) << 4)|(itemp[index+1] >> 4)];
+        *optr++ = table[((itemp[index+1] & 0x0f) << 2)|(itemp[index+2] >> 6)];
+        *optr++ = table[  itemp[index+2] & 0x3f];
+      }
+
+    /*
+     * Special handling for incomplete lines
+     */
+
+    if (index != count)
+      {
+        if (encoding == B64ENCODED)
+          {
+            if (count - index == 2)
+              {
+                *optr++ = table[itemp[index] >> 2];
+                *optr++ = table[((itemp[index  ] & 0x03) << 4) |
+                                ((itemp[index+1] & 0xf0) >> 4)];
+                *optr++ = table[((itemp[index+1] & 0x0f) << 2)];
+                *optr++ = '=';
+              }
+            else if (count - index == 1)
+              {
+                *optr++ = table[ itemp[index] >> 2];
+                *optr++ = table[(itemp[index] & 0x03) << 4];
+                *optr++ = '=';
+                *optr++ = '=';
+              }
+            llen += 4;
+          }
+        else if (encoding == UU_ENCODED || encoding == XX_ENCODED)
+          {
+            if (count - index == 2)
+              {
+                *optr++ = table[itemp[index] >> 2];
+                *optr++ = table[((itemp[index  ] & 0x03) << 4) |
+                                ( itemp[index+1] >> 4)];
+                *optr++ = table[((itemp[index+1] & 0x0f) << 2)];
+                *optr++ = table[0];
+              }
+            else if (count - index == 1)
+              {
+                *optr++ = table[ itemp[index] >> 2];
+                *optr++ = table[(itemp[index] & 0x03) << 4];
+                *optr++ = table[0];
+                *optr++ = table[0];
+              }
+            llen += 4;
+          }
+      }
+
+    /*
+     * end of line
+     */
+
+    tptr = eolstring;
+
+    while (*tptr)
+      *optr++ = *tptr++;
+
+    *optr++ = '\0';
+    llen   += strlen ((char *) eolstring);
+
+    if (fwrite (otemp, 1, llen, outfile) != llen)
+      return UURET_IOERR;
+
+    line++;
+  }
+  return UURET_OK;
+}
+
+/*
+ * Encode as MIME multipart/mixed sub-message.
+ */
+
+int UUEXPORT
+UUEncodeMulti (FILE *outfile, FILE *infile, char *infname, int encoding,
+               char *outfname, char *mimetype, int filemode)
+{
+  mimemap *miter=mimetable;
+  struct stat finfo;
+  int res, themode;
+  FILE *theifile;
+  char *ptr;
+  crc32_t crc;
+  crc32_t *crcptr=NULL;
+
+  if (outfile==NULL ||
+      (infile == NULL && infname==NULL) ||
+      (outfname==NULL && infname==NULL) ||
+      (encoding!=UU_ENCODED&&encoding!=XX_ENCODED&&encoding!=B64ENCODED&&
+       encoding!=PT_ENCODED&&encoding!=QP_ENCODED&&encoding!=YENC_ENCODED))
+    {
+      UUMessage (uuencode_id, __LINE__, UUMSG_ERROR,
+                 uustring (S_PARM_CHECK), "UUEncodeMulti()");
+      return UURET_ILLVAL;
+    }
+
+  progress.action = 0;
+
+  if (infile==NULL)
+    {
+      if (stat (infname, &finfo) == -1)
+        {
+          UUMessage (uuencode_id, __LINE__, UUMSG_ERROR,
+                     uustring (S_NOT_STAT_FILE),
+                     infname, strerror (uu_errno=errno));
+          return UURET_IOERR;
+        }
+      if ((theifile = fopen (infname, "rb")) == NULL)
+        {
+          UUMessage (uuencode_id, __LINE__, UUMSG_ERROR,
+                     uustring (S_NOT_OPEN_FILE),
+                     infname, strerror (uu_errno=errno));
+          return UURET_IOERR;
+        }
+      themode = (filemode) ? filemode : ((int) finfo.st_mode & 0777);
+      progress.fsize = (long) finfo.st_size;
+    }
+  else
+    {
+      if (fstat (fileno (infile), &finfo) != 0)
+        {
+          themode  = (filemode)?filemode:0644;
+          progress.fsize = -1;
+        }
+      else
+        {
+          themode = (int) finfo.st_mode & 0777;
+          progress.fsize = (long) finfo.st_size;
+        }
+      theifile = infile;
+    }
+
+  if (progress.fsize < 0)
+    progress.fsize = -1;
+
+  _FP_strncpy (progress.curfile, (outfname)?outfname:infname, 2048);
+
+  progress.partno   = 1;
+  progress.numparts = 1;
+  progress.percent  = 0;
+  progress.foffset  = 0;
+  progress.action   = UUACT_ENCODING;
+
+  /*
+   * If not given from outside, select an appropriate Content-Type by
+   * looking at the file's extension. If it is unknown, default to
+   * Application/Octet-Stream
+   */
+
+  if (mimetype == NULL)
+    {
+      if ((ptr = _FP_strrchr ((outfname)?outfname:infname, '.')))
+        {
+          while (miter->extension && _FP_stricmp (ptr+1, miter->extension) != 0)
+            miter++;
+          mimetype = miter->mimetype;
+        }
+    }
+
+  if (mimetype == NULL && (encoding == PT_ENCODED || encoding == QP_ENCODED))
+    {
+      mimetype = "text/plain";
+    }
+
+  /*
+   * print sub-header
+   */
+
+  if (encoding != YENC_ENCODED)
+    {
+      fprintf (outfile, "Content-Type: %s%s",
+               (mimetype)?mimetype:"Application/Octet-Stream",
+               eolstring);
+      fprintf (outfile, "Content-Transfer-Encoding: %s%s",
+               CTE_TYPE(encoding), eolstring);
+      fprintf (outfile, "Content-Disposition: attachment; filename=\"%s\"%s",
+               UUFNameFilter ((outfname)?outfname:infname), eolstring);
+      fprintf (outfile, "%s", eolstring);
+    }
+
+  if (encoding == UU_ENCODED || encoding == XX_ENCODED)
+    {
+      fprintf (outfile, "begin %o %s%s",
+               (themode) ? themode : 0644,
+               UUFNameFilter ((outfname)?outfname:infname),
+               eolstring);
+    }
+  else if (encoding == YENC_ENCODED)
+    {
+      crc = crc32(0L, Z_NULL, 0);
+      crcptr = &crc;
+      if (progress.fsize == -1)
+        {
+          fprintf (outfile, "=ybegin line=128 name=%s%s",
+                   UUFNameFilter ((outfname)?outfname:infname),
+                   eolstring);
+        }
+      else
+        {
+          fprintf (outfile, "=ybegin line=128 size=%ld name=%s%s",
+                   progress.fsize,
+                   UUFNameFilter ((outfname)?outfname:infname),
+                   eolstring);
+        }
+    }
+
+  if ((res = UUEncodeStream (outfile, theifile, encoding, 0, crcptr, NULL)) != UURET_OK)
+    {
+      if (res != UURET_CANCEL)
+        {
+          UUMessage (uuencode_id, __LINE__, UUMSG_ERROR,
+                     uustring (S_ERR_ENCODING),
+                     UUFNameFilter ((infname)?infname:outfname),
+                     (res==UURET_IOERR)?strerror(uu_errno):UUstrerror(res));
+        }
+      progress.action = 0;
+      return res;
+    }
+
+  if (encoding == UU_ENCODED || encoding == XX_ENCODED)
+    {
+      fprintf (outfile, "%c%s",
+               (encoding==UU_ENCODED) ? UUEncodeTable[0] : XXEncodeTable[0],
+               eolstring);
+      fprintf (outfile, "end%s", eolstring);
+    }
+  else if (encoding == YENC_ENCODED)
+    {
+      if (progress.fsize == -1)
+        {
+          fprintf (outfile, "=yend crc32=%08lx%s",
+                   crc,
+                   eolstring);
+        }
+      else
+        {
+          fprintf (outfile, "=yend size=%ld crc32=%08lx%s",
+                   progress.fsize,
+                   crc,
+                   eolstring);
+        }
+    }
+
+  /*
+   * empty line at end does no harm
+   */
+
+  fprintf (outfile, "%s", eolstring);
+
+  if (infile==NULL)
+    fclose (theifile);
+
+  progress.action = 0;
+  return UURET_OK;
+}
+
+/*
+ * Encode as MIME message/partial
+ */
+
+int UUEXPORT
+UUEncodePartial (FILE *outfile, FILE *infile,
+                 char *infname, int encoding,
+                 char *outfname, char *mimetype,
+                 int filemode, int partno, long linperfile, unsigned long* crcptr)
+{
+  mimemap *miter=mimetable;
+  static FILE *theifile;
+  int themode, numparts;
+  struct stat finfo;
+  long thesize;
+  char *ptr;
+  int res;
+  crc32_t pcrc;
+  crc32_t *pcrcptr=NULL;
+
+  if ((outfname==NULL&&infname==NULL) || partno<=0 ||
+      (infile == NULL&&infname==NULL) || outfile==NULL ||
+      (encoding!=UU_ENCODED&&encoding!=XX_ENCODED&&encoding!=B64ENCODED&&
+       encoding!=PT_ENCODED&&encoding!=QP_ENCODED&&encoding!=YENC_ENCODED))
+    {
+//    UUMessage (uuencode_id, __LINE__, UUMSG_ERROR,
+//	       uustring (S_PARM_CHECK), "UUEncodePartial()");
+      return UURET_ILLVAL;
+    }
+
+  /*
+   * The first part needs a set of headers
+   */
+
+  progress.action = 0;
+
+  if (partno == 1)
+    {
+      *crcptr = crc32(0L, Z_NULL, 0);
+
+      if (infile==NULL)
+        {
+          if (stat (infname, &finfo) == -1)
+            {
+              UUMessage (uuencode_id, __LINE__, UUMSG_ERROR,
+                         uustring (S_NOT_STAT_FILE),
+                         infname, strerror (uu_errno=errno));
+              return UURET_IOERR;
+            }
+          if ((theifile = fopen (infname, "rb")) == NULL)
+            {
+              UUMessage (uuencode_id, __LINE__, UUMSG_ERROR,
+                         uustring (S_NOT_OPEN_FILE),
+                         infname, strerror (uu_errno=errno));
+              return UURET_IOERR;
+            }
+          if (linperfile <= 0)
+            numparts = 1;
+          else
+            numparts = (int) (((long)finfo.st_size+(linperfile*bpl[encoding]-1))/
+                              (linperfile*bpl[encoding]));
+
+          themode  = (filemode) ? filemode : ((int) finfo.st_mode & 0777);
+          thesize  = (long) finfo.st_size;
+        }
+      else
+        {
+          if (fstat (fileno (infile), &finfo) != 0)
+            {
+              UUMessage (uuencode_id, __LINE__, UUMSG_WARNING,
+                         uustring (S_STAT_ONE_PART));
+              numparts = 1;
+              themode  = (filemode)?filemode:0644;
+              thesize  = -1;
+            }
+          else
+            {
+              if (linperfile <= 0)
+                numparts = 1;
+              else
+                numparts = (int) (((long)finfo.st_size+(linperfile*bpl[encoding]-1))/
+                                  (linperfile*bpl[encoding]));
+
+              themode =  (int) finfo.st_mode & 0777;
+              thesize = (long) finfo.st_size;
+            }
+          theifile = infile;
+        }
+
+      _FP_strncpy (progress.curfile, (outfname)?outfname:infname, 2048);
+
+      progress.totsize  = (thesize>=0) ? thesize : -1;
+      progress.partno   = 1;
+      progress.numparts = numparts;
+      progress.percent  = 0;
+      progress.foffset  = 0;
+
+      /*
+       * If not given from outside, select an appropriate Content-Type by
+       * looking at the file's extension. If it is unknown, default to
+       * Application/Octet-Stream
+       */
+
+      if (mimetype == NULL)
+        {
+          if ((ptr = _FP_strrchr ((outfname)?outfname:infname, '.')))
+            {
+              while (miter->extension && _FP_stricmp (ptr+1, miter->extension) != 0)
+                miter++;
+              mimetype = miter->mimetype;
+            }
+        }
+
+      if (mimetype == NULL && (encoding==PT_ENCODED || encoding==QP_ENCODED))
+        {
+          mimetype = "text/plain";
+        }
+
+      /*
+       * print sub-header
+       */
+
+      if (encoding != YENC_ENCODED)
+        {
+          fprintf (outfile, "MIME-Version: 1.0%s", eolstring);
+          fprintf (outfile, "Content-Type: %s%s",
+                   (mimetype)?mimetype:"Application/Octet-Stream",
+                   eolstring);
+          fprintf (outfile, "Content-Transfer-Encoding: %s%s",
+                   CTE_TYPE(encoding), eolstring);
+          fprintf (outfile, "Content-Disposition: attachment; filename=\"%s\"%s",
+                   UUFNameFilter ((outfname)?outfname:infname), eolstring);
+        }
+
+      fprintf (outfile, "%s", eolstring);
+
+      /*
+       * for the first part of UU or XX messages, print a begin line
+       */
+
+      if (encoding == UU_ENCODED || encoding == XX_ENCODED)
+        {
+          fprintf (outfile, "begin %o %s%s",
+                   (themode) ? themode : ((filemode)?filemode:0644),
+                   UUFNameFilter ((outfname)?outfname:infname), eolstring);
+        }
+    }
+  if (encoding == YENC_ENCODED)
+    {
+      pcrc = crc32(0L, Z_NULL, 0);
+      pcrcptr = &pcrc;
+      if (numparts != 1)
+        {
+          if (progress.totsize == -1)
+            {
+              fprintf (outfile, "=ybegin part=%d line=128 name=%s%s",
+                       partno,
+                       UUFNameFilter ((outfname)?outfname:infname),
+                       eolstring);
+            }
+          else
+            {
+              fprintf (outfile, "=ybegin part=%d line=128 size=%ld name=%s%s",
+                       partno,
+                       progress.totsize,
+                       UUFNameFilter ((outfname)?outfname:infname),
+                       eolstring);
+            }
+
+          fprintf (outfile, "=ypart begin=%ld end=%ld%s",
+                   (partno-1)*linperfile*128+1,
+                   (partno*linperfile*128) < progress.totsize ?
+                   (partno*linperfile*128) : progress.totsize,
+                   eolstring);
+        }
+      else
+        {
+          if (progress.totsize == -1)
+            {
+              fprintf (outfile, "=ybegin line=128 name=%s%s",
+                       UUFNameFilter ((outfname)?outfname:infname),
+                       eolstring);
+            }
+          else
+            {
+              fprintf (outfile, "=ybegin line=128 size=%ld name=%s%s",
+                       progress.totsize,
+                       UUFNameFilter ((outfname)?outfname:infname),
+                       eolstring);
+            }
+        }
+    }
+
+  /*
+   * update progress information
+   */
+
+  progress.partno  = partno;
+  progress.percent = 0;
+  progress.foffset = ftell (theifile);
+
+  if (progress.totsize <= 0)
+    progress.fsize = -1;
+  else if (linperfile <= 0)
+    progress.fsize = progress.totsize;
+  else if (progress.foffset+linperfile*bpl[encoding] > progress.totsize)
+    progress.fsize = progress.totsize - progress.foffset;
+  else
+    progress.fsize = linperfile*bpl[encoding];
+
+  progress.action  = UUACT_ENCODING;
+
+  if ((res = UUEncodeStream (outfile, theifile, encoding, linperfile,
+                             crcptr, pcrcptr)) != UURET_OK)
+    {
+      if (infile==NULL) fclose (theifile);
+      if (res != UURET_CANCEL)
+        {
+          UUMessage (uuencode_id, __LINE__, UUMSG_ERROR,
+                     uustring (S_ERR_ENCODING),
+                     UUFNameFilter ((outfname)?outfname:infname),
+                     (res==UURET_IOERR)?strerror(uu_errno):UUstrerror (res));
+        }
+      progress.action = 0;
+      return res;
+    }
+
+  /*
+   * print end line
+   */
+
+  if (feof (theifile) &&
+      (encoding == UU_ENCODED || encoding == XX_ENCODED))
+    {
+      fprintf (outfile, "%c%s",
+               (encoding==UU_ENCODED) ? UUEncodeTable[0] : XXEncodeTable[0],
+               eolstring);
+      fprintf (outfile, "end%s", eolstring);
+    }
+  else if (encoding == YENC_ENCODED)
+    {
+      if (numparts != 1)
+        {
+          fprintf (outfile, "=yend size=%ld part=%d pcrc32=%08lx",
+                   (partno*linperfile*128) < progress.totsize ?
+                   linperfile*128 : (progress.totsize-(partno-1)*linperfile*128),
+                   partno,
+                   pcrc);
+        }
+      else
+        {
+          fprintf (outfile, "=yend size=%ld",
+                   progress.totsize);
+        }
+      if (feof (theifile))
+        fprintf (outfile, " crc32=%08lx", *crcptr);
+      fprintf (outfile, "%s", eolstring);
+    }
+
+  /*
+   * empty line at end does no harm
+   */
+
+  if (encoding != PT_ENCODED && encoding != QP_ENCODED)
+    {
+      fprintf (outfile, "%s", eolstring);
+    }
+
+  if (infile==NULL)
+    {
+      if (res != UURET_OK)
+        {
+          progress.action = 0;
+          fclose (theifile);
+          return res;
+        }
+      if (feof (theifile))
+        {
+          progress.action = 0;
+          fclose (theifile);
+          return UURET_OK;
+        }
+      return UURET_CONT;
+    }
+
+  /*
+   * leave progress.action as-is
+   */
+
+  return UURET_OK;
+}
+
+int UUEXPORT
+UUEncodePartial_byFSize (FILE *outfile, FILE *infile,
+                         char *infname, int encoding,
+                         char *outfname, char *mimetype,
+                         int filemode, int partno, long bpf, unsigned long* crcptr)
+{
+  mimemap *miter=mimetable;
+  static FILE *theifile;
+  int themode, numparts;
+  struct stat finfo;
+  long thesize;
+  char *ptr;
+  int res;
+  crc32_t pcrc;
+  crc32_t *pcrcptr=NULL;
+
+  if ((outfname==NULL&&infname==NULL) || partno<=0 ||
+      (infile == NULL&&infname==NULL) || outfile==NULL ||
+      (encoding!=UU_ENCODED&&encoding!=XX_ENCODED&&encoding!=B64ENCODED&&
+       encoding!=PT_ENCODED&&encoding!=QP_ENCODED&&encoding!=YENC_ENCODED))
+    {
+      return UURET_ILLVAL;
+    }
+
+  /*
+   * The first part needs a set of headers
+   */
+
+  progress.action = 0;
+
+  if (partno == 1)
+    {
+      *crcptr = crc32(0L, Z_NULL, 0);
+
+      if (infile==NULL)
+        {
+          if (stat (infname, &finfo) == -1)
+          {
+            UUMessage (uuencode_id, __LINE__, UUMSG_ERROR,
+                       uustring (S_NOT_STAT_FILE),
+                       infname, strerror (uu_errno=errno));
+            return UURET_IOERR;
+          }
+          if ((theifile = fopen (infname, "rb")) == NULL)
+          {
+            UUMessage (uuencode_id, __LINE__, UUMSG_ERROR,
+                       uustring (S_NOT_OPEN_FILE),
+                       infname, strerror (uu_errno=errno));
+            return UURET_IOERR;
+          }
+
+          if (finfo.st_size <= bpf)
+            numparts = 1;
+          else
+            numparts = (int) (ceil((double)finfo.st_size / (double)bpf));
+
+          themode  = (filemode) ? filemode : ((int) finfo.st_mode & 0777);
+          thesize  = (long) finfo.st_size;
+        }
+      else
+        {
+          if (fstat (fileno (infile), &finfo) != 0)
+            {
+              UUMessage (uuencode_id, __LINE__, UUMSG_WARNING,
+                         uustring (S_STAT_ONE_PART));
+              numparts = 1;
+              themode  = (filemode)?filemode:0644;
+              thesize  = -1;
+            }
+          else
+            {
+              themode =  (int) finfo.st_mode & 0777;
+              thesize = (long) finfo.st_size;
+
+              if (finfo.st_size <= bpf)
+                numparts = 1;
+              else
+                numparts = (int) (ceill(thesize / bpf));
+
+
+            }
+          theifile = infile;
+        }
+
+      _FP_strncpy (progress.curfile, (outfname)?outfname:infname, 2048);
+
+      progress.totsize  = (thesize>=0) ? thesize : -1;
+      progress.partno   = 1;
+      progress.numparts = numparts;
+      progress.percent  = 0;
+      progress.foffset  = 0;
+
+      /*
+       * If not given from outside, select an appropriate Content-Type by
+       * looking at the file's extension. If it is unknown, default to
+       * Application/Octet-Stream
+       */
+
+      if (mimetype == NULL)
+        {
+          if ((ptr = _FP_strrchr ((outfname)?outfname:infname, '.')))
+            {
+              while (miter->extension && _FP_stricmp (ptr+1, miter->extension) != 0)
+                miter++;
+              mimetype = miter->mimetype;
+            }
+        }
+
+      if (mimetype == NULL && (encoding==PT_ENCODED || encoding==QP_ENCODED))
+        {
+          mimetype = "text/plain";
+        }
+
+
+      /*
+       * print sub-header
+       */
+
+//      if (encoding != YENC_ENCODED)
+//      {
+//        fprintf (outfile, "MIME-Version: 1.0%s", eolstring);
+//        fprintf (outfile, "Content-Type: Message/Partial; number=%d; total=%d;%s",
+//               partno, numparts, eolstring);
+//        fprintf (outfile, "Content-Transfer-Encoding: %s%s",
+//                 CTE_TYPE(encoding), eolstring);
+//        fprintf (outfile, "Content-Disposition: attachment; filename=\"%s\"%s",
+//                 UUFNameFilter ((outfname)?outfname:infname), eolstring);
+//      }
+//      fprintf (outfile, "%s", eolstring);
+
+      /*
+       * for the first part of UU or XX messages, print a begin line
+       */
+
+      if (encoding == UU_ENCODED || encoding == XX_ENCODED)
+        {
+          fprintf (outfile, "begin %o %s%s",
+                   (themode) ? themode : ((filemode)?filemode:0644),
+                   UUFNameFilter ((outfname)?outfname:infname), eolstring);
+        }
+    }
+
+  if (encoding == YENC_ENCODED)
+    {
+      pcrc = crc32(0L, Z_NULL, 0);
+      pcrcptr = &pcrc;
+      if (numparts != 1)
+        {
+          if (progress.totsize == -1)
+            {
+              fprintf (outfile, "=ybegin part=%d line=128 name=%s%s",
+                       partno,
+                       UUFNameFilter ((outfname)?outfname:infname),
+                       eolstring);
+            }
+          else
+            {
+              fprintf (outfile, "=ybegin part=%d line=128 size=%ld name=%s%s",
+                       partno,
+                       progress.totsize,
+                       UUFNameFilter ((outfname)?outfname:infname),
+                       eolstring);
+            }
+
+          fprintf (outfile, "=ypart begin=%ld end=%ld%s",
+                   (partno-1)*bpf,
+                   (partno*bpf) < progress.totsize ?
+                   (partno*bpf) : progress.totsize,
+                   eolstring);
+        }
+      else
+        {
+
+          if (progress.totsize == -1)
+            {
+              fprintf (outfile, "=ybegin line=128 name=%s%s",
+                       UUFNameFilter ((outfname)?outfname:infname),
+                       eolstring);
+            }
+          else
+            {
+              fprintf (outfile, "=ybegin line=128 size=%ld name=%s%s",
+                       progress.totsize,
+                       UUFNameFilter ((outfname)?outfname:infname),
+                       eolstring);
+            }
+        }
+    }
+
+  /*
+   * update progress information
+   */
+
+  progress.partno  = partno;
+  progress.percent = 0;
+  progress.foffset = ftell (theifile);
+
+  if (progress.totsize <= 0)
+    progress.fsize = -1;
+  else if (bpf <= 0)
+    progress.fsize = progress.totsize;
+  else if (progress.foffset+ bpf > progress.totsize)
+    progress.fsize = progress.totsize - progress.foffset;
+  else
+    progress.fsize = bpf;
+
+  progress.action  = UUACT_ENCODING;
+
+  if ((res = UUEncodeStream_byFSize (outfile, theifile, encoding, bpf,
+                             crcptr, pcrcptr)) != UURET_OK)
+  {
+    if (infile==NULL) fclose (theifile);
+    if (res != UURET_CANCEL)
+      {
+        UUMessage (uuencode_id, __LINE__, UUMSG_ERROR,
+                   uustring (S_ERR_ENCODING),
+                   UUFNameFilter ((outfname)?outfname:infname),
+                   (res==UURET_IOERR)?strerror(uu_errno):UUstrerror (res));
+      }
+    progress.action = 0;
+    return res;
+  }
+
+  /*
+   * print end line
+   */
+
+  if (feof (theifile) &&
+      (encoding == UU_ENCODED || encoding == XX_ENCODED))
+    {
+      fprintf (outfile, "%c%s",
+               (encoding==UU_ENCODED) ? UUEncodeTable[0] : XXEncodeTable[0],
+               eolstring);
+      fprintf (outfile, "end%s", eolstring);
+    }
+  else if (encoding == YENC_ENCODED)
+  {
+    if (numparts != 1)
+      {
+        fprintf (outfile, "=yend size=%ld part=%d pcrc32=%08lx",
+                 (partno*bpf) < progress.totsize ?
+                 bpf : (progress.totsize-(partno-1)*bpf),
+                 partno,
+                 pcrc);
+      }
+    else
+      {
+        fprintf (outfile, "=yend size=%ld",
+                 progress.totsize);
+      }
+    if (feof (theifile))
+      fprintf (outfile, " crc32=%08lx", *crcptr);
+    fprintf (outfile, "%s", eolstring);
+  }
+
+  /*
+   * empty line at end does no harm
+   */
+
+  if (encoding != PT_ENCODED && encoding != QP_ENCODED)
+  {
+    fprintf (outfile, "%s", eolstring);
+  }
+
+  if (infile==NULL)
+  {
+    if (res != UURET_OK)
+      {
+        progress.action = 0;
+        fclose (theifile);
+        return res;
+      }
+    if (feof (theifile))
+      {
+        progress.action = 0;
+        fclose (theifile);
+        return UURET_OK;
+      }
+    return UURET_CONT;
+  }
+
+  /*
+   * leave progress.action as-is
+   */
+
+  return UURET_OK;
+}
+
+/*
+ * send output to a stream, don't do any headers at all
+ */
+
+int UUEXPORT
+UUEncodeToStream (FILE *outfile, FILE *infile,
+                  char *infname, int encoding,
+                  char *outfname, int filemode)
+{
+  struct stat finfo;
+  FILE *theifile;
+  int themode;
+  int res;
+  crc32_t crc;
+  crc32_t *crcptr=NULL;
+
+  if (outfile==NULL ||
+      (infile == NULL&&infname==NULL) ||
+      (outfname==NULL&&infname==NULL) ||
+      (encoding!=UU_ENCODED&&encoding!=XX_ENCODED&&encoding!=B64ENCODED&&
+       encoding!=PT_ENCODED&&encoding!=QP_ENCODED&&encoding!=YENC_ENCODED))
+    {
+      UUMessage (uuencode_id, __LINE__, UUMSG_ERROR,
+                 uustring (S_PARM_CHECK), "UUEncodeToStream()");
+      return UURET_ILLVAL;
+    }
+
+  progress.action = 0;
+
+  if (infile==NULL)
+    {
+      if (stat (infname, &finfo) == -1)
+        {
+          UUMessage (uuencode_id, __LINE__, UUMSG_ERROR,
+                     uustring (S_NOT_STAT_FILE),
+                     infname, strerror (uu_errno=errno));
+          return UURET_IOERR;
+        }
+      if ((theifile = fopen (infname, "rb")) == NULL)
+        {
+          UUMessage (uuencode_id, __LINE__, UUMSG_ERROR,
+                     uustring (S_NOT_OPEN_FILE),
+                     infname, strerror (uu_errno=errno));
+          return UURET_IOERR;
+        }
+      themode = (filemode) ? filemode : ((int) finfo.st_mode & 0777);
+      progress.fsize = (long) finfo.st_size;
+    }
+  else
+    {
+      if (fstat (fileno (infile), &finfo) == -1)
+        {
+          /* gotta live with it */
+          themode = 0644;
+          progress.fsize = -1;
+        }
+      else
+        {
+          themode = (filemode) ? filemode : ((int) finfo.st_mode & 0777);
+          progress.fsize = (long) finfo.st_size;
+        }
+      theifile = infile;
+    }
+
+  if (progress.fsize < 0)
+    progress.fsize = -1;
+
+  _FP_strncpy (progress.curfile, (outfname)?outfname:infname, 2048);
+
+  progress.partno   = 1;
+  progress.numparts = 1;
+  progress.percent  = 0;
+  progress.foffset  = 0;
+  progress.action   = UUACT_ENCODING;
+
+  if (encoding == UU_ENCODED || encoding == XX_ENCODED)
+    {
+      fprintf (outfile, "begin %o %s%s",
+               (themode) ? themode : 0644,
+               UUFNameFilter ((outfname)?outfname:infname),
+               eolstring);
+    }
+  else if (encoding == YENC_ENCODED)
+    {
+      crc = crc32(0L, Z_NULL, 0);
+      crcptr = &crc;
+      if (progress.fsize == -1)
+        {
+          fprintf (outfile, "=ybegin line=128 name=%s%s",
+                   UUFNameFilter ((outfname)?outfname:infname),
+                   eolstring);
+        }
+      else
+        {
+          fprintf (outfile, "=ybegin line=128 size=%ld name=%s%s",
+                   progress.fsize,
+                   UUFNameFilter ((outfname)?outfname:infname),
+                   eolstring);
+        }
+    }
+
+  if ((res = UUEncodeStream (outfile, theifile, encoding, 0, crcptr, NULL)) != UURET_OK)
+    {
+      if (res != UURET_CANCEL)
+        {
+          UUMessage (uuencode_id, __LINE__, UUMSG_ERROR,
+                     uustring (S_ERR_ENCODING),
+                     UUFNameFilter ((infname)?infname:outfname),
+                     (res==UURET_IOERR)?strerror(uu_errno):UUstrerror (res));
+        }
+      progress.action = 0;
+      return res;
+    }
+
+  if (encoding == UU_ENCODED || encoding == XX_ENCODED)
+    {
+      fprintf (outfile, "%c%s",
+               (encoding==UU_ENCODED) ? UUEncodeTable[0] : XXEncodeTable[0],
+               eolstring);
+      fprintf (outfile, "end%s", eolstring);
+    }
+  else if (encoding == YENC_ENCODED)
+    {
+      if (progress.fsize == -1)
+        {
+          fprintf (outfile, "=yend crc32=%08lx%s",
+                   crc,
+                   eolstring);
+        }
+      else
+        {
+          fprintf (outfile, "=yend size=%ld crc32=%08lx%s",
+                   progress.fsize,
+                   crc,
+                   eolstring);
+        }
+    }
+
+  /*
+   * empty line at end does no harm
+   */
+
+  fprintf (outfile, "%s", eolstring);
+
+  if (infile==NULL) fclose (theifile);
+  progress.action = 0;
+
+  return UURET_OK;
+}
+
+/*
+ * Encode to files on disk, don't generate any headers
+ */
+
+int UUEXPORT
+UUEncodeToFile (FILE *infile, char *infname, int encoding,
+                char *outfname, char *diskname, long linperfile)
+{
+  int part, numparts, len, filemode, res;
+  char *oname=NULL, *optr, *ptr;
+  FILE *theifile, *outfile;
+  struct stat finfo;
+  crc32_t pcrc, crc;
+  crc32_t *pcrcptr=NULL, *crcptr=NULL;
+
+  if ((diskname==NULL&&infname==NULL) ||
+      (outfname==NULL&&infname==NULL) || (infile==NULL&&infname==NULL) ||
+      (encoding!=UU_ENCODED&&encoding!=XX_ENCODED&&encoding!=B64ENCODED&&
+       encoding!=PT_ENCODED&&encoding!=QP_ENCODED&&encoding!=YENC_ENCODED))
+    {
+      UUMessage (uuencode_id, __LINE__, UUMSG_ERROR,
+                 uustring (S_PARM_CHECK), "UUEncodeToFile()");
+      return UURET_ILLVAL;
+    }
+
+  if (diskname)
+    {
+      if ((ptr = strchr (diskname, '/')) == NULL)
+        ptr = strchr (diskname, '\\');
+      if (ptr)
+        {
+          len = strlen (diskname) + ((uuencodeext)?strlen(uuencodeext):3) + 5;
+
+          if ((oname = malloc (len)) == NULL)
+            {
+              UUMessage (uuencode_id, __LINE__, UUMSG_ERROR,
+                         uustring (S_OUT_OF_MEMORY), len);
+              return UURET_NOMEM;
+            }
+          sprintf (oname, "%s", diskname);
+        }
+      else
+        {
+          len = ((uusavepath)?strlen(uusavepath):0) + strlen (diskname)
+                + ((uuencodeext)?strlen(uuencodeext):0) + 5;
+
+          if ((oname = malloc (len)) == NULL)
+            {
+              UUMessage (uuencode_id, __LINE__, UUMSG_ERROR,
+                         uustring (S_OUT_OF_MEMORY), len);
+              return UURET_NOMEM;
+            }
+          sprintf (oname, "%s%s", (uusavepath)?uusavepath:"", diskname);
+        }
+    }
+  else
+    {
+      len = ((uusavepath) ? strlen (uusavepath) : 0) +
+            strlen(UUFNameFilter(infname)) +
+            ((uuencodeext)?strlen(uuencodeext):0) + 5;
+
+      if ((oname = malloc (len)) == NULL)
+        {
+          UUMessage (uuencode_id, __LINE__, UUMSG_ERROR,
+                     uustring (S_OUT_OF_MEMORY), len);
+          return UURET_NOMEM;
+        }
+      optr = UUFNameFilter (infname);
+      sprintf (oname, "%s%s",
+               (uusavepath)?uusavepath:"",
+               (*optr=='.')?optr+1:optr);
+    }
+
+  /*
+   * optr points after the last dot, so that we can print the part number
+   * there.
+   */
+
+  optr = _FP_strrchr (oname, '.');
+  if (optr==NULL || strchr (optr, '/')!=NULL || strchr (optr, '\\')!=NULL)
+    {
+      optr = oname + strlen (oname);
+      *optr++ = '.';
+    }
+  else if (optr==oname || *(optr-1)=='/' || *(optr-1)=='\\')
+    {
+      optr = oname + strlen (oname);
+      *optr++ = '.';
+    }
+  else
+    optr++;
+
+  progress.action = 0;
+
+  if (infile==NULL)
+    {
+      if (stat (infname, &finfo) == -1)
+        {
+          UUMessage (uuencode_id, __LINE__, UUMSG_ERROR,
+                     uustring (S_NOT_STAT_FILE),
+                     infname, strerror (uu_errno=errno));
+          _FP_free (oname);
+          return UURET_IOERR;
+        }
+      if ((theifile = fopen (infname, "rb")) == NULL)
+        {
+          UUMessage (uuencode_id, __LINE__, UUMSG_ERROR,
+                     uustring (S_NOT_OPEN_FILE),
+                     infname, strerror (uu_errno=errno));
+          _FP_free (oname);
+          return UURET_IOERR;
+        }
+      if (linperfile <= 0)
+        numparts = 1;
+      else
+        numparts = (int) (((long)finfo.st_size + (linperfile*bpl[encoding]-1)) /
+                          (linperfile*bpl[encoding]));
+
+      filemode = (int) finfo.st_mode & 0777;
+      progress.totsize = (long) finfo.st_size;
+    }
+  else
+    {
+      if (fstat (fileno (infile), &finfo) == -1)
+        {
+          /* gotta live with it */
+          filemode = 0644;
+          numparts = -1;
+          progress.totsize = -1;
+        }
+      else
+        {
+          if (linperfile <= 0)
+            numparts = 1;
+          else
+            numparts = (int) (((long)finfo.st_size+(linperfile*bpl[encoding]-1))/
+                              (linperfile*bpl[encoding]));
+
+          filemode = (int) finfo.st_mode & 0777;
+          progress.totsize = -1;
+        }
+      theifile = infile;
+    }
+
+  _FP_strncpy (progress.curfile, (outfname)?outfname:infname, 2048);
+
+  progress.totsize  = (progress.totsize<0) ? -1 : progress.totsize;
+  progress.numparts = numparts;
+
+  for (part=1; !feof (theifile); part++)
+    {
+      /*
+       * Attach extension
+       */
+      if (progress.numparts==1 && progress.totsize!=-1 && uuencodeext!=NULL)
+        strcpy  (optr, uuencodeext);
+      else
+        sprintf (optr, "%03d", part);
+
+      /*
+       * check if target file exists
+       */
+
+      if (!uu_overwrite)
+        {
+          if (stat (oname, &finfo) == 0)
+            {
+              UUMessage (uuencode_id, __LINE__, UUMSG_ERROR,
+                         uustring (S_TARGET_EXISTS), oname);
+              if (infile==NULL) fclose (theifile);
+              progress.action = 0;
+              free (oname);
+              return UURET_EXISTS;
+            }
+        }
+
+      /*
+       * update progress information
+       */
+
+      progress.action  = 0;
+      progress.partno  = part;
+      progress.percent = 0;
+      progress.foffset = ftell (theifile);
+
+      if (progress.totsize == -1)
+        progress.fsize = -1;
+      else if (linperfile <= 0)
+        progress.fsize = progress.totsize;
+      else if (progress.foffset+linperfile*bpl[encoding] > progress.totsize)
+        progress.fsize = progress.totsize - progress.foffset;
+      else
+        progress.fsize = linperfile*bpl[encoding];
+
+      progress.action  = UUACT_ENCODING;
+
+      if ((outfile = fopen (oname, "w")) == NULL)
+        {
+          UUMessage (uuencode_id, __LINE__, UUMSG_ERROR,
+                     uustring (S_NOT_OPEN_TARGET),
+                     oname, strerror (uu_errno = errno));
+          if (infile==NULL) fclose (theifile);
+          progress.action = 0;
+          free (oname);
+          return UURET_IOERR;
+        }
+
+      if (encoding != YENC_ENCODED)
+        {
+          fprintf (outfile, "%s", eolstring);
+          fprintf (outfile, "_=_ %s", eolstring);
+          if (numparts == -1)
+            fprintf (outfile, "_=_ Part %03d of file %s%s",
+                     part, UUFNameFilter ((outfname)?outfname:infname),
+                     eolstring);
+          else
+            fprintf (outfile, "_=_ Part %03d of %03d of file %s%s",
+                     part, numparts,
+                     UUFNameFilter ((outfname)?outfname:infname),
+                     eolstring);
+          fprintf (outfile, "_=_ %s", eolstring);
+          fprintf (outfile, "%s", eolstring);
+        }
+
+      if (part==1 && (encoding == UU_ENCODED || encoding == XX_ENCODED))
+        {
+          fprintf (outfile, "begin %o %s%s",
+                   (filemode)?filemode : 0644,
+                   UUFNameFilter ((outfname)?outfname:infname),
+                   eolstring);
+        }
+      else if (encoding == YENC_ENCODED)
+        {
+          if (!crcptr)
+            {
+              crc = crc32(0L, Z_NULL, 0);
+              crcptr = &crc;
+            }
+          pcrc = crc32(0L, Z_NULL, 0);
+          pcrcptr = &pcrc;
+          if (numparts != 1)
+            {
+              if (progress.totsize == -1)
+                {
+                  fprintf (outfile, "=ybegin part=%d line=128 name=%s%s",
+                           part,
+                           UUFNameFilter ((outfname)?outfname:infname),
+                           eolstring);
+                }
+              else
+                {
+                  fprintf (outfile, "=ybegin part=%d line=128 size=%ld name=%s%s",
+                           part,
+                           progress.totsize,
+                           UUFNameFilter ((outfname)?outfname:infname),
+                           eolstring);
+                }
+
+              fprintf (outfile, "=ypart begin=%ld end=%ld%s",
+                       (part-1)*linperfile*128+1,
+                       (part*linperfile*128) < progress.totsize ?
+                       (part*linperfile*128) : progress.totsize,
+                       eolstring);
+            }
+          else
+            {
+              if (progress.totsize == -1)
+                {
+                  fprintf (outfile, "=ybegin line=128 name=%s%s",
+                           UUFNameFilter ((outfname)?outfname:infname),
+                           eolstring);
+                }
+              else
+                {
+                  fprintf (outfile, "=ybegin line=128 size=%ld name=%s%s",
+                           progress.totsize,
+                           UUFNameFilter ((outfname)?outfname:infname),
+                           eolstring);
+                }
+            }
+        }
+
+      if ((res = UUEncodeStream (outfile, theifile,
+                                 encoding, linperfile, crcptr, pcrcptr)) != UURET_OK)
+        {
+          if (res != UURET_CANCEL)
+            {
+              UUMessage (uuencode_id, __LINE__, UUMSG_ERROR,
+                         uustring (S_ERR_ENCODING),
+                         UUFNameFilter ((infname)?infname:outfname),
+                         (res==UURET_IOERR)?strerror(uu_errno):UUstrerror (res));
+            }
+          if (infile==NULL) fclose (theifile);
+          progress.action = 0;
+          fclose (outfile);
+          unlink (oname);
+          _FP_free (oname);
+          return res;
+        }
+
+      if (feof (theifile) &&
+          (encoding == UU_ENCODED || encoding == XX_ENCODED))
+        {
+          fprintf (outfile, "%c%s",
+                   (encoding==UU_ENCODED) ? UUEncodeTable[0] : XXEncodeTable[0],
+                   eolstring);
+          fprintf (outfile, "end%s", eolstring);
+        }
+      else if (encoding == YENC_ENCODED)
+        {
+          if (numparts != 1)
+            {
+              fprintf (outfile, "=yend size=%ld part=%d pcrc32=%08lx",
+                       (part*linperfile*128) < progress.totsize ?
+                       linperfile*128 : (progress.totsize-(part-1)*linperfile*128),
+                       part,
+                       pcrc);
+            }
+          else
+            {
+              fprintf (outfile, "=yend size=%ld",
+                       progress.totsize);
+            }
+          if (feof (theifile))
+            fprintf (outfile, " crc32=%08lx", crc);
+          fprintf (outfile, "%s", eolstring);
+        }
+
+      /*
+       * empty line at end does no harm
+       */
+
+      fprintf (outfile, "%s", eolstring);
+      fclose  (outfile);
+    }
+
+  if (infile==NULL) fclose (theifile);
+  progress.action = 0;
+  _FP_free (oname);
+  return UURET_OK;
+}
+
+/*
+ * Encode a MIME Mail message or Newsgroup posting and send to a
+ * stream. Still needs a somewhat smart MDA, since we only gene-
+ * rate a minimum set of headers.
+ */
+
+int UUEXPORT
+UUE_PrepSingle (FILE *outfile, FILE *infile,
+                char *infname, int encoding,
+                char *outfname, int filemode,
+                char *destination, char *from,
+                char *subject, int isemail)
+{
+  return UUE_PrepSingleExt (outfile, infile,
+                            infname, encoding,
+                            outfname, filemode,
+                            destination, from,
+                            subject, NULL,
+                            isemail);
+}
+
+int UUEXPORT
+UUE_PrepSingleExt (FILE *outfile, FILE *infile,
+                   char *infname, int encoding,
+                   char *outfname, int filemode,
+                   char *destination, char *from,
+                   char *subject, char *replyto,
+                   int isemail)
+{
+  mimemap *miter=mimetable;
+  char *subline, *oname;
+  char *mimetype, *ptr;
+  int res, len;
+
+  if ((outfname==NULL&&infname==NULL) || (infile==NULL&&infname==NULL) ||
+      (encoding!=UU_ENCODED&&encoding!=XX_ENCODED&&encoding!=B64ENCODED&&
+       encoding!=PT_ENCODED&&encoding!=QP_ENCODED&&encoding!=YENC_ENCODED))
+    {
+      UUMessage (uuencode_id, __LINE__, UUMSG_ERROR,
+                 uustring (S_PARM_CHECK), "UUE_PrepSingle()");
+      return UURET_ILLVAL;
+    }
+
+  oname = UUFNameFilter ((outfname)?outfname:infname);
+  len   = ((subject)?strlen(subject):0) + strlen(oname) + 40;
+
+  if ((ptr = _FP_strrchr (oname, '.')))
+    {
+      while (miter->extension && _FP_stricmp (ptr+1, miter->extension) != 0)
+        miter++;
+      mimetype = miter->mimetype;
+    }
+  else
+    mimetype = NULL;
+
+  if (mimetype == NULL && (encoding == PT_ENCODED || encoding == QP_ENCODED))
+    {
+      mimetype = "text/plain";
+    }
+
+  if ((subline = (char *) malloc (len)) == NULL)
+    {
+      UUMessage (uuencode_id, __LINE__, UUMSG_ERROR,
+                 uustring (S_OUT_OF_MEMORY), len);
+      return UURET_NOMEM;
+    }
+
+  if (encoding == YENC_ENCODED)
+    {
+      if (subject)
+        sprintf (subline, "- %s - %s (001/001)", oname, subject);
+      else
+        sprintf (subline, "- %s - (001/001)", oname);
+    }
+  else
+    {
+      if (subject)
+        sprintf (subline, "%s (001/001) - [ %s ]", subject, oname);
+      else
+        sprintf (subline, "[ %s ] (001/001)", oname);
+    }
+
+  if (from)
+    {
+      fprintf (outfile, "From: %s%s", from, eolstring);
+    }
+  if (destination)
+    {
+      fprintf (outfile, "%s: %s%s",
+               (isemail)?"To":"Newsgroups",
+               destination, eolstring);
+    }
+
+  fprintf (outfile, "Subject: %s%s", subline, eolstring);
+
+  if (replyto)
+    {
+      fprintf (outfile, "Reply-To: %s%s", replyto, eolstring);
+    }
+
+  if (encoding != YENC_ENCODED)
+    {
+      fprintf (outfile, "MIME-Version: 1.0%s", eolstring);
+      fprintf (outfile, "Content-Type: %s; name=\"%s\"%s",
+               (mimetype)?mimetype:"Application/Octet-Stream",
+               UUFNameFilter ((outfname)?outfname:infname),
+               eolstring);
+      fprintf (outfile, "Content-Transfer-Encoding: %s%s",
+               CTE_TYPE(encoding), eolstring);
+    }
+
+  fprintf (outfile, "%s", eolstring);
+
+  res = UUEncodeToStream (outfile, infile, infname, encoding,
+                          outfname, filemode);
+
+  _FP_free (subline);
+  return res;
+}
+
+int UUEXPORT
+UUE_PrepPartial (FILE *outfile, FILE *infile,
+                 char *infname, int encoding,
+                 char *outfname, int filemode,
+                 int partno, long linperfile, long filesize,
+                 char *destination, char *from, char *subject,
+                 int isemail)
+{
+  return UUE_PrepPartialExt (outfile, infile,
+                             infname, encoding,
+                             outfname, filemode,
+                             partno, linperfile, filesize,
+                             destination,
+                             from, subject, NULL,
+                             isemail);
+}
+
+int UUEXPORT
+UUE_PrepPartialExt (FILE *outfile, FILE *infile,
+                    char *infname, int encoding,
+                    char *outfname, int filemode,
+                    int partno, long linperfile, long filesize,
+                    char *destination,
+                    char *from, char *subject, char *replyto,
+                    int isemail)
+{
+  static int numparts, themode;
+  static char mimeid[64];
+  static FILE *theifile;
+  struct stat finfo;
+  char *subline, *oname;
+  long thesize;
+  int res, len;
+  static crc32_t crc;
+  crc32_t *crcptr=NULL;
+
+  if ((outfname==NULL&&infname==NULL) || (infile==NULL&&infname==NULL) ||
+      (encoding!=UU_ENCODED&&encoding!=XX_ENCODED&&encoding!=B64ENCODED&&
+       encoding!=PT_ENCODED&&encoding!=QP_ENCODED&&encoding!=YENC_ENCODED))
+    {
+      UUMessage (uuencode_id, __LINE__, UUMSG_ERROR,
+                 uustring (S_PARM_CHECK), "UUE_PrepPartial()");
+      return UURET_ILLVAL;
+    }
+
+  oname = UUFNameFilter ((outfname)?outfname:infname);
+  len   = ((subject)?strlen(subject):0) + strlen (oname) + 40;
+
+  /*
+   * if first part, get information about the file
+   */
+
+  if (partno == 1)
+    {
+      if (infile==NULL)
+        {
+          if (stat (infname, &finfo) == -1)
+            {
+              UUMessage (uuencode_id, __LINE__, UUMSG_ERROR,
+                         uustring (S_NOT_STAT_FILE),
+                         infname, strerror (uu_errno=errno));
+              return UURET_IOERR;
+            }
+          if ((theifile = fopen (infname, "rb")) == NULL)
+            {
+              UUMessage (uuencode_id, __LINE__, UUMSG_ERROR,
+                         uustring (S_NOT_OPEN_FILE),
+                         infname, strerror (uu_errno=errno));
+              return UURET_IOERR;
+            }
+          if (linperfile <= 0)
+            numparts = 1;
+          else
+            numparts = (int) (((long)finfo.st_size+(linperfile*bpl[encoding]-1))/
+                              (linperfile*bpl[encoding]));
+
+          themode  = (filemode) ? filemode : ((int) finfo.st_mode & 0777);
+          thesize  = (long) finfo.st_size;
+        }
+      else
+        {
+          if (fstat (fileno (infile), &finfo) != 0)
+            {
+              if (filesize <= 0)
+                {
+                  UUMessage (uuencode_id, __LINE__, UUMSG_WARNING,
+                             uustring (S_STAT_ONE_PART));
+                  numparts = 1;
+                  themode  = (filemode)?filemode:0644;
+                  thesize  = -1;
+                }
+              else
+                {
+                  if (linperfile <= 0)
+                    numparts = 1;
+                  else
+                    numparts = (int) ((filesize+(linperfile*bpl[encoding]-1))/
+                                      (linperfile*bpl[encoding]));
+
+                  themode  = (filemode)?filemode:0644;
+                  thesize  = filesize;
+                }
+            }
+          else
+            {
+              if (linperfile <= 0)
+                numparts = 1;
+              else
+                numparts = (int) (((long)finfo.st_size+(linperfile*bpl[encoding]-1))/
+                                  (linperfile*bpl[encoding]));
+
+              filemode = (int) finfo.st_mode & 0777;
+              thesize  = (long) finfo.st_size;
+            }
+          theifile = infile;
+        }
+
+      /*
+       * if there's one part only, don't use Message/Partial
+       */
+
+      if (numparts == 1)
+        {
+          if (infile==NULL) fclose (theifile);
+          return UUE_PrepSingleExt (outfile, infile, infname, encoding,
+                                    outfname, filemode, destination,
+                                    from, subject, replyto, isemail);
+        }
+
+      /*
+       * we also need a unique ID
+       */
+
+      sprintf (mimeid, "UUDV-%ld.%ld.%s",
+               (long) time(NULL), thesize,
+               (strlen(oname)>16)?"oops":oname);
+    }
+
+  if ((subline = (char *) malloc (len)) == NULL)
+    {
+      UUMessage (uuencode_id, __LINE__, UUMSG_ERROR,
+                 uustring (S_OUT_OF_MEMORY), len);
+      if (infile==NULL) fclose (theifile);
+      return UURET_NOMEM;
+    }
+
+
+  if (encoding == YENC_ENCODED)
+    {
+      if (partno == 1)
+        crc = crc32(0L, Z_NULL, 0);
+      crcptr = &crc;
+      if (subject)
+        sprintf (subline, "- %s - %s (%03d/%03d)", oname, subject,
+                 partno, numparts);
+      else
+        sprintf (subline, "- %s - (%03d/%03d)", oname,
+                 partno, numparts);
+    }
+  else
+    {
+      if (subject)
+        sprintf (subline, "%s (%03d/%03d) - [ %s ]",
+                 subject, partno, numparts, oname);
+      else
+        sprintf (subline, "[ %s ] (%03d/%03d)",
+                 oname, partno, numparts);
+    }
+
+  if (from)
+    {
+      fprintf (outfile, "From: %s%s", from, eolstring);
+    }
+
+  if (destination)
+    {
+      fprintf (outfile, "%s: %s%s",
+               (isemail)?"To":"Newsgroups",
+               destination, eolstring);
+    }
+
+  fprintf (outfile, "Subject: %s%s", subline, eolstring);
+
+  if (replyto)
+    {
+      fprintf (outfile, "Reply-To: %s%s", replyto, eolstring);
+    }
+
+  if (encoding != YENC_ENCODED)
+    {
+      fprintf (outfile, "MIME-Version: 1.0%s", eolstring);
+      fprintf (outfile, "Content-Type: Message/Partial; number=%d; total=%d;%s",
+               partno, numparts, eolstring);
+      fprintf (outfile, "\tid=\"%s\"%s",
+               mimeid, eolstring);
+    }
+
+  fprintf (outfile, "%s", eolstring);
+
+  res = UUEncodePartial (outfile, theifile,
+                         infname, encoding,
+                         (outfname)?outfname:infname, NULL,
+                         themode, partno, linperfile, crcptr);
+
+  _FP_free (subline);
+
+  if (infile==NULL)
+    {
+      if (res != UURET_OK)
+        {
+          fclose (theifile);
+          return res;
+        }
+      if (feof (theifile))
+        {
+          fclose (theifile);
+          return UURET_OK;
+        }
+      return UURET_CONT;
+    }
+
+  return res;
+}
diff --git a/yenclib/uunconc.c b/yenclib/uunconc.c
new file mode 100644
index 0000000..42f9753
--- /dev/null
+++ b/yenclib/uunconc.c
@@ -0,0 +1,1718 @@
+/*
+ * This file is part of uudeview, the simple and friendly multi-part multi-
+ * file uudecoder  program  (c) 1994-2001 by Frank Pilhofer. The author may
+ * be contacted at fp fpx de
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * These are the functions that are responsible for decoding. The
+ * original idea is from a freeware utility called "uunconc", and
+ * few lines of this code may still bear a remote resemblance to
+ * its code. If you are the author or know him, contact me.
+ * This program could only decode one multi-part, uuencoded file
+ * where the parts were in order. Base64, XX and BinHex decoding,
+ * support for multi-files and part-ordering covered by myself.
+ **/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib.h>
+
+#ifdef SYSTEM_WINDLL
+#include <windows.h>
+#endif
+#ifdef SYSTEM_OS2
+#include <os2.h>
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include <crc32.h>
+#include <uudeview.h>
+#include <uuint.h>
+#include <fptools.h>
+#include <uustring.h>
+
+char * uunconc_id = "$Id$";
+
+/* for braindead systems */
+#ifndef SEEK_SET
+#ifdef L_BEGIN
+#define SEEK_SET L_BEGIN
+#else
+#define SEEK_SET 0
+#endif
+#endif
+
+/*
+ * decoder states
+ */
+
+#define BEGIN     (1)
+#define DATA      (2)
+#define END       (3)
+#define DONE      (4)
+
+/*
+ * mallocable areas
+ */
+
+char *uunconc_UUxlat;
+char *uunconc_UUxlen;
+char *uunconc_B64xlat;
+char *uunconc_XXxlat;
+char *uunconc_BHxlat;
+char *uunconc_save;
+
+/*
+ * decoding translation tables and line length table
+ */
+
+static int      * UUxlen;	/* initialized in UUInitConc()    */
+static int      * UUxlat;	/* from the malloc'ed areas above */
+static int      * B64xlat;
+static int      * XXxlat;
+static int     	* BHxlat;
+
+/*
+ * buffer for decoding
+ */
+
+static char *save[3];
+
+/*
+ * mallocable areas
+ */
+
+char *uuncdl_fulline;
+char *uuncdp_oline;
+
+/*
+ * Return information for QuickDecode
+ */
+
+static int uulboundary;
+
+/*
+ * To prevent warnings when using a char as index into an array
+ */
+
+#define ACAST(s)	((int)(unsigned char)(s))
+
+/*
+ * Initialize decoding tables
+ */
+
+void
+UUInitConc (void)
+{
+  int i, j;
+
+  /*
+   * Update pointers
+   */
+  UUxlen  = (int *) uunconc_UUxlen;
+  UUxlat  = (int *) uunconc_UUxlat;
+  B64xlat = (int *) uunconc_B64xlat;
+  XXxlat  = (int *) uunconc_XXxlat;
+  BHxlat  = (int *) uunconc_BHxlat;
+
+  save[0] = uunconc_save;
+  save[1] = uunconc_save + 256;
+  save[2] = uunconc_save + 512;
+
+  /* prepare decoding translation table */
+  for(i = 0; i < 256; i++)
+    UUxlat[i] = B64xlat[i] = XXxlat[i] = BHxlat[i] = -1;
+
+  /*
+   * At some time I received a file which used lowercase characters for
+   * uuencoding. This shouldn't be, but let's accept it. Must take special
+   * care that this doesn't break xxdecoding. This is giving me quite a
+   * headache. If this one file hadn't been a Pocahontas picture, I might
+   * have ignored it for good.
+   */
+
+  for (i = ' ', j = 0; i < ' ' + 64; i++, j++)
+    UUxlat[i] /* = UUxlat[i+64] */ = j;
+  for (i = '`', j = 0; i < '`' + 32; i++, j++)
+    UUxlat[i] = j;
+
+  /* add special cases */
+  UUxlat['`'] = UUxlat[' '];
+  UUxlat['~'] = UUxlat['^'];
+
+  /* prepare line length table */
+  UUxlen[0] = 1;
+  for(i = 1, j = 5; i <= 61; i += 3, j += 4)
+    UUxlen[i] = UUxlen[i+1] = UUxlen[i+2] = j;
+
+  /* prepare other tables */
+  for (i=0; i<64; i++) {
+    B64xlat[ACAST(B64EncodeTable[i])] = i;
+    XXxlat [ACAST(XXEncodeTable [i])] = i;
+    BHxlat [ACAST(BHEncodeTable [i])] = i;
+  }
+}
+
+/*
+ * Workaround for Netscape
+ */
+
+/*
+ * Determines whether Netscape may have broken up a data line (by
+ * inserting a newline). This only seems to happen after <a in a
+ * href statement
+ */
+
+static int
+UUBrokenByNetscape (char *string)
+{
+  char *ptr;
+  int len;
+
+  if (string==NULL || (len=strlen(string))<3)
+    return 0;
+
+  if ((ptr = _FP_stristr (string, "<a href=")) != NULL) {
+    if (_FP_stristr (string, "</a>") > ptr)
+      return 2;
+  }
+
+  ptr = string + len;
+
+  while (len && (*(ptr-1)=='\015' || *(ptr-1)=='\012')) {
+    ptr--; len--;
+  }
+  if (len<3)         return 0;
+  if (*--ptr == ' ') ptr--;
+  ptr--;
+
+  if (_FP_strnicmp (ptr, "<a", 2) == 0)
+    return 1;
+
+  return 0;
+}
+
+/*
+ * Try to repair a Netscape-corrupted line of data.
+ * This must only be called on corrupted lines, since non-Netscape
+ * data may even _get_ corrupted by this procedure.
+ *
+ * Some checks are included multiply to speed up the procedure. For
+ * example: (*p1!='<' || strnicmp(p1,"</a>",4)). If the first expression
+ * becomes true, the costly function isn't called :-)
+ *
+ * Since '<', '>', '&' might even be replaced by their html equivalents
+ * in href strings, I'm now using two passes, the first one for &amp; + co,
+ * the second one for hrefs.
+ */
+
+static int
+UUNetscapeCollapse (char *string)
+{
+  char *p1=string, *p2=string;
+  int res = 0;
+
+  if (string==NULL)
+    return 0;
+
+  /*
+   * First pass
+   */
+  while (*p1) {
+    if (*p1 == '&') {
+      if      (_FP_strnicmp (p1, "&amp;", 5) == 0) { p1+=5; *p2++='&'; res=1; }
+      else if (_FP_strnicmp (p1, "&lt;",  4) == 0) { p1+=4; *p2++='<'; res=1; }
+      else if (_FP_strnicmp (p1, "&gt;",  4) == 0) { p1+=4; *p2++='>'; res=1; }
+      else *p2++ = *p1++;
+    }
+    else *p2++ = *p1++;
+  }
+  *p2 = '\0';
+  /*
+   * Second pass
+   */
+  p1 = p2 = string;
+
+  while (*p1) {
+    if (*p1 == '<') {
+      if ((_FP_strnicmp (p1, "<ahref=", 7) == 0 ||
+	   _FP_strnicmp (p1, "<a href=",8) == 0) &&
+	  (_FP_strstr (p1, "</a>") != 0 || _FP_strstr (p1, "</A>") != 0)) {
+	while (*p1 && *p1!='>')        p1++;
+	if (*p1=='\0' || *(p1+1)!='<') return 0;
+	p1++;
+	while (*p1 && (*p1!='<' || _FP_strnicmp(p1,"</a>",4)!=0)) {
+	  *p2++ = *p1++;
+	}
+	if (_FP_strnicmp(p1,"</a>",4) != 0)
+	  return 0;
+	p1+=4;
+	res=1;
+      }
+      else
+	*p2++ = *p1++;
+    }
+    else
+      *p2++ = *p1++;
+  }
+  *p2 = '\0';
+
+  return res;
+}
+
+/*
+ * The second parameter is 0 if we are still searching for encoded data,
+ * otherwise it indicates the encoding we're using right now. If we're
+ * still in the searching stage, we must be a little more strict in
+ * deciding for or against encoding; there's too much plain text looking
+ * like encoded data :-(
+ */
+
+int
+UUValidData (char *ptr, int encoding, int *bhflag)
+{
+  int i=0, j, len=0, suspicious=0, flag=0;
+  char *s = ptr;
+
+  if ((s == NULL) || (*s == '\0')) {
+    return 0;              /* bad string */
+  }
+
+  while (*s && *s!='\012' && *s!='\015') {
+    s++;
+    len++;
+    i++;
+  }
+
+  if (i == 0)
+    return 0;
+
+  switch (encoding) {
+  case UU_ENCODED:
+    goto _t_UU;
+  case XX_ENCODED:
+    goto _t_XX;
+  case B64ENCODED:
+    goto _t_B64;
+  case BH_ENCODED:
+    goto _t_Binhex;
+  case YENC_ENCODED:
+    return YENC_ENCODED;
+  }
+
+ _t_Binhex:                 /* Binhex Test */
+  len = i; s = ptr;
+
+  /*
+   * bhflag notes the state we're in. Within the data, it's 1. If we're
+   * still looking for the initial :, it's 0
+   */
+  if (*bhflag == 0 && *s != ':') {
+    if (encoding==BH_ENCODED) return 0;
+    goto _t_B64;
+  }
+  else if (*bhflag == 0 /* *s == ':' */) {
+    s++; len--;
+  }
+
+  while (len && BHxlat[ACAST(*s)] != -1) {
+    len--; s++;
+  }
+
+  /* allow space characters at the end of the line if we are sure */
+  /* that this is Binhex encoded data or the line was long enough */
+
+  flag = (*s == ':') ? 0 : 1;
+
+  if (*s == ':' && len>0) {
+    s++; len--;
+  }
+  if (((i>=60 && len<=10) || encoding) && *s==' ') {
+    while (len && *s==' ') {
+      s++; len--;
+    }
+  }
+
+  /*
+   * BinHex data shall have exactly 64 characters (except the last
+   * line). We ignore everything with less than 40 characters to
+   * be flexible
+   */
+
+  if (len != 0 || (flag && i < 40)) {
+    if (encoding==BH_ENCODED) return 0;
+    goto _t_B64;
+  }
+
+  *bhflag = flag;
+
+  return BH_ENCODED;
+
+ _t_B64:                    /* Base64 Test */
+  len = i; s = ptr;
+
+  /*
+   * Face it: there _are_ Base64 lines that are not a multiple of four
+   * in length :-(
+   *
+   * if (len%4)
+   *   goto _t_UU;
+   */
+
+  while (len--) {
+    if (*s < 0 || (B64xlat[ACAST(*s)] == -1 && *s != '=')) {
+      /* allow space characters at the end of the line if we are sure */
+      /* that this is Base64 encoded data or the line was long enough */
+      if (((i>=60 && len<=10) || encoding) && *s++==' ') {
+	while (*s==' ' && len) s++;
+	if (len==0) return B64ENCODED;
+      }
+      if (encoding==B64ENCODED) return 0;
+      goto _t_UU;
+    }
+    else if (*s == '=') {   /* special case at end */
+      /* if we know this is B64encoded, allow spaces at end of line */
+      s++;
+      if (*s=='=' && len>=1) {
+	len--; s++;
+      }
+      if (encoding && len && *s==' ') {
+	while (len && *s==' ') {
+	  s++; len--;
+	}
+      }
+      if (len != 0) {
+	if (encoding==B64ENCODED) return 0;
+	goto _t_UU;
+      }
+      return B64ENCODED;
+    }
+    s++;
+  }
+  return B64ENCODED;
+
+ _t_UU:
+  len = i; s = ptr;
+
+  if (UUxlat[ACAST(*s)] == -1) {    /* uutest */
+    if (encoding==UU_ENCODED) return 0;
+    goto _t_XX;
+  }
+
+  j = UUxlen[UUxlat[ACAST(*s)]];
+
+  if (len-1 == j)	    /* remove trailing character */
+    len--;
+  if (len != j) {
+    switch (UUxlat[ACAST(*s)]%3) {
+    case 1:
+      if (j-2 == len) j-=2;
+      break;
+    case 2:
+      if (j-1 == len) j-=1;
+      break;
+    }
+  }
+
+  /*
+   * some encoders are broken with respect to encoding the last line of
+   * a file and produce extraoneous characters beyond the expected EOL
+   * So were not too picky here about the last line, as long as it's longer
+   * than necessary and shorter than the maximum
+   * this tolerance broke the xxdecoding, because xxencoded data was
+   * detected as being uuencoded :( so don't accept 'h' as first character
+   * also, if the first character is lowercase, don't accept the line to
+   * have space characters. the only encoder I've heard of which uses
+   * lowercase characters at least accepts the special case of encoding
+   * 0 as `. The strchr() shouldn't be too expensive here as it's only
+   * evaluated if the first character is lowercase, which really shouldn't
+   * be in uuencoded text.
+   */
+  if (len != j &&
+      ((ptr[0] == '-' && ptr[1] == '-' && strstr(ptr,"part")!=NULL) ||
+       !(*ptr != 'M' && *ptr != 'h' &&
+	 len > j && len <= UUxlen[UUxlat['M']]))) {
+    if (encoding==UU_ENCODED) return 0;
+    goto _t_XX;             /* bad length */
+  }
+
+  if (len != j || islower (*ptr)) {
+    /*
+     * if we are not in a 'uuencoded' state, don't allow the line to have
+     * space characters at all. if we know we _are_ decoding uuencoded
+     * data, the rest of the line, beyond the length of encoded data, may
+     * have spaces.
+     */
+    if (encoding != UU_ENCODED)
+      if (strchr (ptr, ' ') != NULL)
+	goto _t_XX;
+
+/*  suspicious = 1;    we're careful here REMOVED 0.4.15 __FP__ */
+    len        = j;
+  }
+
+  while (len--) {
+    if (*s < 0 || UUxlat[ACAST(*s++)] < 0) {
+      if (encoding==UU_ENCODED) return 0;
+      goto _t_XX;           /* bad code character */
+    }
+    if (*s == ' ' && suspicious) {
+      if (encoding==UU_ENCODED) return 0;
+      goto _t_XX;           /* this line looks _too_ suspicious */
+    }
+  }
+  return UU_ENCODED;        /* data is valid */
+
+ _t_XX:                     /* XX Test */
+  len = i; s = ptr;
+
+  if (XXxlat[ACAST(*s)] == -1)
+    return 0;
+
+  j = UUxlen[XXxlat[ACAST(*s)]];   /* Same line length table as UUencoding */
+
+  if (len-1 == j)	    /* remove trailing character */
+    len--;
+  if (len != j)
+    switch (UUxlat[ACAST(*s)]%3) {
+    case 1:
+      if (j-2 == len) j-=2;
+      break;
+    case 2:
+      if (j-1 == len) j-=1;
+      break;
+    }
+  /*
+   * some encoders are broken with respect to encoding the last line of
+   * a file and produce extraoneous characters beyond the expected EOL
+   * So were not too picky here about the last line, as long as it's longer
+   * than necessary and shorter than the maximum
+   */
+  if (len != j && !(*ptr != 'h' && len > j && len <= UUxlen[UUxlat['h']]))
+    return 0;               /* bad length */
+
+  while(len--) {
+    if(*s < 0 || XXxlat[ACAST(*s++)] < 0) {
+      return 0;             /* bad code character */
+    }
+  }
+  return XX_ENCODED;        /* data is valid */
+}
+
+/*
+ * This function may be called upon a line that does not look like
+ * valid encoding on first sight, but might be erroneously encoded
+ * data from Netscape, Lynx or MS Exchange. We might need to read
+ * a new line from the stream, which is why we need the FILE.
+ * Returns the type of encoded data if successful or 0 otherwise.
+ */
+
+int
+UURepairData (FILE *datei, char *line, int encoding, int *bhflag)
+{
+  int nflag, vflag=0, safety=42;
+  char *ptr;
+
+  nflag = UUBrokenByNetscape (line);
+
+  while (vflag == 0 && nflag && safety--) {
+    if (nflag == 1) {		/* need next line to repair */
+      if (strlen (line) > 250)
+	break;
+      ptr = line + strlen (line);
+      while (ptr>line && (*(ptr-1)=='\015' || *(ptr-1)=='\012'))
+	ptr--;
+      if (_FP_fgets (ptr, 255-(ptr-line), datei) == NULL)
+	break;
+    }
+    else {			/* don't need next line to repair */
+    }
+    if (UUNetscapeCollapse (line)) {
+      if ((vflag = UUValidData (line, encoding, bhflag)) == 0)
+	nflag = UUBrokenByNetscape (line);
+    }
+    else
+      nflag = 0;
+  }
+  /*
+   * Sometimes, a line is garbled even without it being split into
+   * the next line. Then we try this in our despair
+   */
+  if (vflag == 0) {
+    if (UUNetscapeCollapse (line))
+      vflag = UUValidData (line, encoding, bhflag);
+  }
+
+  /*
+   * If this line looks uuencoded, but the line is one character short
+   * of a valid line, it was probably broken by MS Exchange. According
+   * to my test cases, there is at most one space character missing;
+   * there are never two spaces together.
+   * If adding a space character helps making this line uuencoded, do
+   * it!
+   */
+
+  if (vflag == 0) {
+    ptr    = line + strlen(line);
+    while (ptr>line && (*(ptr-1)=='\012' || *(ptr-1)=='\015')) {
+      ptr--;
+    }
+    *ptr++ = ' ';
+    *ptr-- = '\0';
+    if ((vflag = UUValidData (line, encoding, bhflag)) != UU_ENCODED) {
+      *ptr  = '\0';
+      vflag = 0;
+    }
+  }
+  return vflag;
+}
+
+/*
+ * Decode a single encoded line using method
+ */
+
+size_t
+UUDecodeLine (char *s, char *d, int method)
+{
+  int i, j, c, cc, count=0, z1, z2, z3, z4;
+  static int leftover=0;
+  int *table;
+
+  /*
+   * for re-initialization
+   */
+
+  if (s == NULL || d == NULL) {
+    leftover = 0;
+    return 0;
+  }
+
+  /*
+   * To shut up gcc -Wall
+   */
+  z1 = z2 = z3 = z4 = 0;
+
+  if (method == UU_ENCODED || method == XX_ENCODED) {
+    if (method == UU_ENCODED)
+      table = UUxlat;
+    else
+      table = XXxlat;
+
+    i = table [ACAST(*s++)];
+    j = UUxlen[i] - 1;
+
+    while(j > 0) {
+      c  = table[ACAST(*s++)] << 2;
+      cc = table[ACAST(*s++)];
+      c |= (cc >> 4);
+
+      if(i-- > 0)
+	d[count++] = c;
+
+      cc <<= 4;
+      c    = table[ACAST(*s++)];
+      cc  |= (c >> 2);
+
+      if(i-- > 0)
+	d[count++] = cc;
+
+      c <<= 6;
+      c |= table[ACAST(*s++)];
+
+      if(i-- > 0)
+	d[count++] = c;
+
+      j -= 4;
+    }
+  }
+  else if (method == B64ENCODED) {
+    if (leftover) {
+      strcpy (uuncdl_fulline+leftover, s);
+      leftover = 0;
+      s        = uuncdl_fulline;
+    }
+
+    while ((z1 = B64xlat[ACAST(*s)]) != -1) {
+      if ((z2 = B64xlat[ACAST(*(s+1))]) == -1) break;
+      if ((z3 = B64xlat[ACAST(*(s+2))]) == -1) break;
+      if ((z4 = B64xlat[ACAST(*(s+3))]) == -1) break;
+
+      d[count++] = (z1 << 2) | (z2 >> 4);
+      d[count++] = (z2 << 4) | (z3 >> 2);
+      d[count++] = (z3 << 6) | (z4);
+
+      s += 4;
+    }
+    if (z1 != -1 && z2 != -1 && *(s+2) == '=') {
+      d[count++] = (z1 << 2) | (z2 >> 4);
+      s+=2;
+    }
+    else if (z1 != -1 && z2 != -1 && z3 != -1 && *(s+3) == '=') {
+      d[count++] = (z1 << 2) | (z2 >> 4);
+      d[count++] = (z2 << 4) | (z3 >> 2);
+      s+=3;
+    }
+    while (B64xlat[ACAST(*s)] != -1)
+      uuncdl_fulline[leftover++] = *s++;
+  }
+  else if (method == BH_ENCODED) {
+    if (leftover) {
+      strcpy (uuncdl_fulline+leftover, s);
+      leftover = 0;
+      s        = uuncdl_fulline;
+    }
+    else if (*s == ':')
+      s++;
+
+    while ((z1 = BHxlat[ACAST(*s)]) != -1) {
+      if ((z2 = BHxlat[ACAST(*(s+1))]) == -1) break;
+      if ((z3 = BHxlat[ACAST(*(s+2))]) == -1) break;
+      if ((z4 = BHxlat[ACAST(*(s+3))]) == -1) break;
+
+      d[count++] = (z1 << 2) | (z2 >> 4);
+      d[count++] = (z2 << 4) | (z3 >> 2);
+      d[count++] = (z3 << 6) | (z4);
+
+      s += 4;
+    }
+    if (z1 != -1 && z2 != -1 && *(s+2) == ':') {
+      d[count++] = (z1 << 2) | (z2 >> 4);
+      s+=2;
+    }
+    else if (z1 != -1 && z2 != -1 && z3 != -1 && *(s+3) == ':') {
+      d[count++] = (z1 << 2) | (z2 >> 4);
+      d[count++] = (z2 << 4) | (z3 >> 2);
+      s+=3;
+    }
+    while (BHxlat[ACAST(*s)] != -1)
+      uuncdl_fulline[leftover++] = *s++;
+  }
+  else if (method == YENC_ENCODED) {
+    while (*s) {
+      if (*s == '=') {
+	if (*++s != '\0') {
+	  d[count++] = (char) ((int) *s - 64 - 42);
+	  s++;
+	}
+      }
+      else if (*s == '\n' || *s == '\r') {
+	s++; /* ignore */
+      }
+      else {
+	d[count++] = (char) ((int) *s++ - 42);
+      }
+    }
+  }
+
+  return count;
+}
+
+/*
+ * ``Decode'' Quoted-Printable text
+ */
+
+static int
+UUDecodeQP (FILE *datain, FILE *dataout, int *state,
+	    long maxpos, int method, int flags,
+	    char *boundary)
+{
+  char *line=uugen_inbuffer, *p1, *p2;
+  int val;
+
+  uulboundary = -1;
+
+  while (!feof (datain) &&
+	 (ftell(datain)<maxpos || flags&FL_TOEND ||
+	  (!(flags&FL_PROPER) && uu_fast_scanning))) {
+    if (_FP_fgets (line, 255, datain) == NULL)
+      break;
+    if (ferror (datain)) {
+      UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
+		 uustring (S_SOURCE_READ_ERR),
+		 strerror (uu_errno = errno));
+      return UURET_IOERR;
+    }
+    line[255] = '\0';
+
+    if (boundary && line[0]=='-' && line[1]=='-' &&
+	strncmp (line+2, boundary, strlen (boundary)) == 0) {
+      if (line[strlen(boundary)+2]=='-')
+	uulboundary = 1;
+      else
+	uulboundary = 0;
+      return UURET_OK;
+    }
+
+    if (UUBUSYPOLL(ftell(datain)-progress.foffset,progress.fsize)) {
+      UUMessage (uunconc_id, __LINE__, UUMSG_NOTE,
+		 uustring (S_DECODE_CANCEL));
+      return UURET_CANCEL;
+    }
+
+    p1 = p2 = line;
+
+    while (*p2) {
+      while (*p2 && *p2 != '=')
+	p2++;
+      if (*p2 == '\0')
+	break;
+      *p2 = '\0';
+      fprintf (dataout, "%s", p1);
+      p1  = ++p2;
+
+      if (isxdigit (*p2) && isxdigit (*(p2+1))) {
+	val  = ((isdigit(*p2))    ?  (*p2-'0')   : (tolower(*p2)-'a'+10)) << 4;
+	val |= ((isdigit(*(p2+1)))?(*(p2+1)-'0') : (tolower(*(p2+1))-'a'+10));
+
+	fputc (val, dataout);
+	p2 += 2;
+	p1  = p2;
+      }
+      else if (*p2 == '\012' || *(p2+1) == '\015') {
+	/* soft line break */
+	*p2 = '\0';
+	break;
+      }
+      else {
+	/* huh? */
+	fputc ('=', dataout);
+      }
+    }
+    /*
+     * p2 points to a nullbyte right after the CR/LF/CRLF
+     */
+    val = 0;
+    while (p2>p1 && isspace (*(p2-1))) {
+      if (*(p2-1) == '\012' || *(p2-1) == '\015')
+	val = 1;
+      p2--;
+    }
+    *p2 = '\0';
+
+    /*
+     * If the part ends directly after this line, the data does not end
+     * with a linebreak. Or, as the docs put it, "the CRLF preceding the
+     * encapsulation line is conceptually attached to the boundary.
+     * So if the part ends here, don't print a line break"
+     */
+    if (val && (!feof (datain) &&
+		(ftell(datain)<maxpos || flags&FL_TOEND ||
+		 (!(flags&FL_PROPER) && uu_fast_scanning))))
+      fprintf (dataout, "%s\n", p1);
+    else
+      fprintf (dataout, "%s", p1);
+  }
+  return UURET_OK;
+}
+
+/*
+ * ``Decode'' plain text. Our job is to properly handle the EOL sequence
+ */
+
+static int
+UUDecodePT (FILE *datain, FILE *dataout, int *state,
+	    long maxpos, int method, int flags,
+	    char *boundary)
+{
+  char *line=uugen_inbuffer, *ptr;
+
+  uulboundary = -1;
+
+  while (!feof (datain) &&
+	 (ftell(datain)<maxpos || flags&FL_TOEND ||
+	  (!(flags&FL_PROPER) && uu_fast_scanning))) {
+    if (_FP_fgets (line, 255, datain) == NULL)
+      break;
+    if (ferror (datain)) {
+      UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
+		 uustring (S_SOURCE_READ_ERR),
+		 strerror (uu_errno = errno));
+      return UURET_IOERR;
+    }
+    line[255] = '\0';
+
+    if (boundary && line[0]=='-' && line[1]=='-' &&
+	strncmp (line+2, boundary, strlen (boundary)) == 0) {
+      if (line[strlen(boundary)+2]=='-')
+	uulboundary = 1;
+      else
+	uulboundary = 0;
+      return UURET_OK;
+    }
+
+    if (UUBUSYPOLL(ftell(datain)-progress.foffset,progress.fsize)) {
+      UUMessage (uunconc_id, __LINE__, UUMSG_NOTE,
+		 uustring (S_DECODE_CANCEL));
+      return UURET_CANCEL;
+    }
+
+    ptr = line + strlen (line);
+
+    while (ptr>line && (*(ptr-1) == '\012' || *(ptr-1) == '\015'))
+      ptr--;
+
+
+    /*
+     * If the part ends directly after this line, the data does not end
+     * with a linebreak. Or, as the docs put it, "the CRLF preceding the
+     * encapsulation line is conceptually attached to the boundary.
+     * So if the part ends here, don't print a line break"
+     */
+    if ((*ptr == '\012' || *ptr == '\015') &&
+	(ftell(datain)<maxpos || flags&FL_TOEND || flags&FL_PARTIAL ||
+	 !boundary || (!(flags&FL_PROPER) && uu_fast_scanning))) {
+      *ptr = '\0';
+      fprintf (dataout, "%s\n", line);
+    }
+    else {
+      *ptr = '\0';
+      fprintf (dataout, "%s", line);
+    }
+  }
+  return UURET_OK;
+}
+
+/*
+ * Decode a single field using method. For the moment, this supports
+ * Base64 and Quoted Printable only, to support RFC 1522 header decoding.
+ * Quit when seeing the RFC 1522 ?= end marker.
+ */
+
+int
+UUDecodeField (char *s, char *d, int method)
+{
+  int z1, z2, z3, z4;
+  int count=0;
+
+  if (method == B64ENCODED) {
+    while ((z1 = B64xlat[ACAST(*s)]) != -1) {
+      if ((z2 = B64xlat[ACAST(*(s+1))]) == -1) break;
+      if ((z3 = B64xlat[ACAST(*(s+2))]) == -1) break;
+      if ((z4 = B64xlat[ACAST(*(s+3))]) == -1) break;
+
+      d[count++] = (z1 << 2) | (z2 >> 4);
+      d[count++] = (z2 << 4) | (z3 >> 2);
+      d[count++] = (z3 << 6) | (z4);
+
+      s+=4;
+    }
+    if (z1 != -1 && z2 != -1 && *(s+2) == '=') {
+      d[count++] = (z1 << 2) | (z2 >> 4);
+      s+=2;
+    }
+    else if (z1 != -1 && z2 != -1 && z3 != -1 && *(s+3) == '=') {
+      d[count++] = (z1 << 2) | (z2 >> 4);
+      d[count++] = (z2 << 4) | (z3 >> 2);
+      s+=3;
+    }
+  }
+  else if (method == QP_ENCODED) {
+    while (*s && (*s != '?' || *(s+1) != '=')) {
+      while (*s && *s != '=' && (*s != '?' || *(s+1) != '=')) {
+	d[count++] = *s++;
+      }
+      if (*s == '=') {
+	if (isxdigit (*(s+1)) && isxdigit (*(s+2))) {
+	  d[count]  = (isdigit (*(s+1)) ? (*(s+1)-'0') : (tolower (*(s+1))-'a'+10)) << 4;
+	  d[count] |= (isdigit (*(s+2)) ? (*(s+2)-'0') : (tolower (*(s+2))-'a'+10));
+	  count++;
+	  s+=3;
+	}
+	else if (*(s+1) == '\012' || *(s+1) == '\015') {
+	  s+=2;
+	}
+	else {
+	  d[count++] = *s++;
+	}
+      }
+    }
+  }
+  else {
+    return -1;
+  }
+
+  d[count] = '\0';
+  return count;
+}
+
+int
+UUDecodePart (FILE *datain, FILE *dataout, int *state,
+	      long maxpos, int method, int flags,
+	      char *boundary)
+{
+  char *line = uugen_fnbuffer;
+  int line_len = UUGEN_FNBUFFER_LEN;
+  char *oline=uuncdp_oline;
+  int warning=0, vlc=0, lc[2], hadct=0;
+  int tc=0, tf=0, vflag, haddata=0, haddh=0;
+  long yefilesize=0, yepartends=0;
+  crc32_t yepartcrc=crc32(0L, Z_NULL, 0);
+  static crc32_t yefilecrc=0;
+  static int bhflag=0;
+  size_t count=0;
+  size_t yepartsize=0;
+  char *ptr;
+
+  if (datain == NULL || dataout == NULL) {
+    yefilecrc = crc32(0L, Z_NULL, 0);
+    bhflag = 0;
+    return UURET_OK;
+  }
+
+  /*
+   * Use specialized functions for QP_ENCODED and PT_ENCODED plaintext
+   */
+
+  if (method == QP_ENCODED)
+    return UUDecodeQP (datain, dataout, state, maxpos,
+		       method, flags, boundary);
+  else if (method == PT_ENCODED)
+    return UUDecodePT (datain, dataout, state, maxpos,
+		       method, flags, boundary);
+
+  lc[0] = lc[1] = 0;
+  vflag = 0;
+
+  uulboundary = -1;
+
+  if (method == YENC_ENCODED) {
+    *state = BEGIN;
+  }
+
+  while (!feof (datain) && *state != DONE &&
+	 (ftell(datain)<maxpos || flags&FL_TOEND || maxpos==-1 ||
+	  (!(flags&FL_PROPER) && uu_fast_scanning))) {
+    if (_FP_fgets (line, line_len, datain) == NULL)
+      break;
+
+    if (ferror (datain)) {
+      UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
+		 uustring (S_SOURCE_READ_ERR),
+		 strerror (uu_errno = errno));
+      return UURET_IOERR;
+    }
+
+    if (line[0]=='\015' || line[0]=='\012') { /* Empty line? */
+      if (*state == DATA &&
+	  (method == UU_ENCODED || method == XX_ENCODED))
+	*state = END;
+
+      /*
+       * if we had a whole block of valid lines before, we reset our
+       * 'valid data' flag, tf. Without this 'if', we'd break decoding
+       * files with interleaved blank lines. The value of 5 is chosen
+       * quite arbitrarly.
+       */
+
+      if (vlc > 5)
+	tf = tc = 0;
+      vlc = 0;
+      continue;
+    }
+
+    /*
+     * Busy Polls
+     */
+
+    if (UUBUSYPOLL(ftell(datain)-progress.foffset,progress.fsize)) {
+      UUMessage (uunconc_id, __LINE__, UUMSG_NOTE,
+		 uustring (S_DECODE_CANCEL));
+      return UURET_CANCEL;
+    }
+
+    /*
+     * try to make sense of data
+     */
+
+    line[255] = '\0'; /* For Safety of string functions */
+    count     =  0;
+
+    if (boundary && line[0]=='-' && line[1]=='-' &&
+	strncmp (line+2, boundary, strlen (boundary)) == 0) {
+      if (line[strlen(boundary)+2]=='-')
+	uulboundary = 1;
+      else
+	uulboundary = 0;
+      return UURET_OK;
+    }
+
+    /*
+     * Use this pseudo-handling only if !FL_PROPER
+     */
+
+    if ((flags&FL_PROPER) == 0) {
+      if (strncmp (line, "BEGIN", 5) == 0 &&
+	  _FP_strstr  (line, "CUT HERE")  && !tf) { /* I hate these lines */
+	tc = tf = vlc = 0;
+	continue;
+      }
+      /* MIME body boundary */
+      if (line[0] == '-' && line[1] == '-' && method == B64ENCODED) {
+	if ((haddata || tc) && (haddh || hadct)) {
+	  *state = DONE;
+	  vlc   = 0;
+	  lc[0] = lc[1] = 0;
+	  continue;
+	}
+	hadct = 0;
+	haddh = 1;
+	continue;
+      }
+      if (_FP_strnicmp (line, "Content-Type", 12) == 0)
+	hadct = 1;
+    }
+
+    if (*state == BEGIN) {
+      if ((method == UU_ENCODED || method == XX_ENCODED) &&
+	  (strncmp      (line, "begin ",       6) == 0 ||
+	   _FP_strnicmp (line, "<pre>begin ", 11) == 0)) { /* for LYNX */
+	*state = DATA;
+	continue;
+      }
+      else if (method == BH_ENCODED && line[0] == ':') {
+	if (UUValidData (line, BH_ENCODED, &bhflag) == BH_ENCODED) {
+	  bhflag = 0;
+	  *state = DATA;
+	}
+	else
+	  continue;
+      }
+      else if (method == YENC_ENCODED &&
+	       strncmp (line, "=ybegin ", 8) == 0 &&
+	       _FP_strstr (line, " name=") != NULL) {
+	*state = DATA;
+
+	if ((ptr = _FP_strstr (line, " size=")) != NULL) {
+	  ptr += 6;
+	  yefilesize = atoi (ptr);
+	}
+	else {
+	  yefilesize = -1;
+	}
+
+	if (_FP_strstr (line, " part=") != NULL) {
+	  if (_FP_fgets (line, line_len, datain) == NULL) {
+	    break;
+	  }
+
+	  if ((ptr = _FP_strstr (line, " end=")) == NULL) {
+	    break;
+	  }
+
+	  yepartends = atoi (ptr + 5);
+	}
+	tf = 1;
+	continue;
+      }
+      else {
+	continue;
+      }
+
+      tc = tf = vlc = 0;
+      lc[0] = lc[1] = 0;
+    }
+    else if ((*state == END) &&
+	     (method == UU_ENCODED || method == XX_ENCODED)) {
+      if (strncmp (line, "end", 3) == 0) {
+	*state = DONE;
+	break;
+      }
+    }
+
+    if (*state == DATA && method == YENC_ENCODED &&
+	strncmp (line, "=yend ", 6) == 0) {
+      if ((ptr = _FP_strstr (line, " pcrc32=")) != NULL) {
+	crc32_t pcrc32 = strtoul (ptr + 8, NULL, 16);
+	if (pcrc32 != yepartcrc) {
+	  UUMessage (uunconc_id, __LINE__, UUMSG_WARNING,
+		     uustring (S_PCRC_MISMATCH), progress.curfile, progress.partno);
+	}
+      }
+      if ((ptr = _FP_strstr (line, " crc32=")) != NULL)
+      {
+	crc32_t fcrc32 = strtoul (ptr + 7, NULL, 16);
+	if (fcrc32 != yefilecrc) {
+	  UUMessage (uunconc_id, __LINE__, UUMSG_WARNING,
+		     uustring (S_CRC_MISMATCH), progress.curfile);
+	}
+      }
+      if ((ptr = _FP_strstr (line, " size=")) != NULL)
+      {
+	size_t size = atol(ptr + 6);
+	if (size != yepartsize && yefilesize != -1) {
+	  if (size != yefilesize)
+	    UUMessage (uunconc_id, __LINE__, UUMSG_WARNING,
+		       uustring (S_PSIZE_MISMATCH), progress.curfile,
+		       progress.partno, yepartsize, size);
+	  else
+	    UUMessage (uunconc_id, __LINE__, UUMSG_WARNING,
+		       uustring (S_SIZE_MISMATCH), progress.curfile,
+		       yepartsize, size);
+	}
+      }
+      if (yepartends == 0 || yepartends >= yefilesize) {
+	*state = DONE;
+      }
+      break;
+    }
+
+    if (*state == DATA || *state == END) {
+      if (method==B64ENCODED && line[0]=='-' && line[1]=='-' && tc) {
+	break;
+      }
+
+      if ((vflag = UUValidData (line, (tf)?method:0, &bhflag)) == 0)
+	vflag = UURepairData (datain, line, (tf)?method:0, &bhflag);
+
+      /*
+       * correct XX/UUencoded lines that were declared Base64
+       */
+
+      if ((method == XX_ENCODED || method == UU_ENCODED) &&
+	  vflag == B64ENCODED) {
+	if (UUValidData (line, method, &bhflag) == method)
+	  vflag = method;
+      }
+
+      if (vflag == method) {
+	if (tf) {
+	  count  = UUDecodeLine (line, oline, method);
+	  if (method == YENC_ENCODED) {
+	    if (yepartends)
+	      yepartcrc = crc32(yepartcrc, (unsigned char*)oline, count);
+	    yefilecrc = crc32(yefilecrc, (unsigned char*)oline, count);
+	    yepartsize += count;
+	  }
+	  vlc++; lc[1]++;
+	}
+	else if (tc == 3) {
+	  count  = UUDecodeLine (save[0], oline,         method);
+	  count += UUDecodeLine (save[1], oline + count, method);
+	  count += UUDecodeLine (save[2], oline + count, method);
+	  count += UUDecodeLine (line,    oline + count, method);
+	  tf     = 1;
+	  tc     = 0;
+
+	  /*
+	   * complain if we had one or two invalid lines amidst of
+	   * correctly encoded data. This usually means that the
+	   * file is in error
+	   */
+
+	  if (lc[1] > 10 && (lc[0] >= 1 && lc[0] <= 2) && !warning) {
+	    UUMessage (uunconc_id, __LINE__, UUMSG_WARNING,
+		       uustring (S_DATA_SUSPICIOUS));
+	    warning=1;
+	  }
+	  lc[0] = 0;
+	  lc[1] = 3;
+	}
+	else {
+	  _FP_strncpy (save[tc++], line, 256);
+	}
+
+	if (method == UU_ENCODED)
+	  *state = (line[0] == 'M') ? DATA : END;
+	else if (method == XX_ENCODED)
+	  *state = (line[0] == 'h') ? DATA : END;
+	else if (method == B64ENCODED)
+	  *state = (strchr (line, '=') == NULL) ? DATA : DONE;
+	else if (method == BH_ENCODED)
+	  *state = (!line[0] || strchr(line+1,':')==NULL)?DATA:DONE;
+      }
+      else {
+	vlc = tf = tc = 0;
+	haddh = 0;
+	lc[0]++;
+      }
+    }
+    else if (*state != DONE) {
+      return UURET_NOEND;
+    }
+
+    if (count) {
+      if (method == BH_ENCODED) {
+	if (UUbhwrite (oline, 1, count, dataout) != count) {
+	  UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
+		     uustring (S_WR_ERR_TEMP),
+		     strerror (uu_errno = errno));
+	  return UURET_IOERR;
+	}
+      }
+      else if (fwrite (oline, 1, count, dataout) != count) {
+	UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
+		   uustring (S_WR_ERR_TEMP),
+		   strerror (uu_errno = errno));
+	return UURET_IOERR;
+      }
+      haddata++;
+      count = 0;
+    }
+  }
+
+  if (*state  == DONE ||
+      (*state == DATA && method == B64ENCODED &&
+       vflag == B64ENCODED && (flags&FL_PROPER || haddh))) {
+    for (tf=0; tf<tc; tf++)
+      count += UUDecodeLine (save[tf], oline + count, method);
+    if (count) {
+      if (method == BH_ENCODED) {
+	if (UUbhwrite (oline, 1, count, dataout) != count) {
+	  UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
+		     uustring (S_WR_ERR_TEMP),
+		     strerror (uu_errno = errno));
+	  return UURET_IOERR;
+	}
+      }
+      else if (fwrite (oline, 1, count, dataout) != count) {
+	UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
+		   uustring (S_WR_ERR_TEMP),
+		   strerror (uu_errno = errno));
+	return UURET_IOERR;
+      }
+    }
+  }
+  return UURET_OK;
+}
+
+/*
+ * this function decodes the file into a temporary file
+ */
+
+int
+UUDecode (uulist *data)
+{
+  int state=BEGIN, part=-1, res=0, hb;
+  long rsize, dsize, numbytes;
+  FILE *datain, *dataout;
+  unsigned char r[8];
+  char *mode, *ntmp, *template;
+  uufile *iter;
+  size_t bytes;
+  int fd;
+
+  if (data == NULL || data->thisfile == NULL)
+    return UURET_ILLVAL;
+
+  if (data->state & UUFILE_TMPFILE)
+    return UURET_OK;
+
+  if (data->state & UUFILE_NODATA)
+    return UURET_NODATA;
+
+  if (data->state & UUFILE_NOBEGIN) {
+    if (!uu_desperate)
+      return UURET_NODATA;
+    UUMessage (uunconc_id, __LINE__, UUMSG_WARNING,
+               uustring(S_DATA_SUSPICIOUS));
+  }
+
+  if (data->uudet == PT_ENCODED)
+    mode = "wt";	/* open text files in text mode */
+  else
+    mode = "wb";	/* otherwise in binary          */
+
+
+  template = g_build_filename (g_get_tmp_dir(), "uuXXXXXX", NULL);
+  data->binfile = strdup (template);
+  fd = g_mkstemp (data->binfile);
+  g_free (template);
+
+  if (fd == -1) {
+    UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
+	       uustring (S_NO_TEMP_NAME));
+    _FP_free (data->binfile);
+    data->binfile = NULL;
+    uu_errno = errno;
+    return UURET_NOMEM;
+  }
+
+  if ((dataout = fdopen (fd, mode)) == NULL) {
+    /*
+     * we couldn't create a temporary file. Usually this means that TMP
+     * and TEMP aren't set
+     */
+    UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
+	       uustring (S_WR_ERR_TARGET),
+	       data->binfile, strerror (uu_errno = errno));
+    _FP_free (data->binfile);
+    data->binfile = NULL;
+    uu_errno = errno;
+    return UURET_IOERR;
+  }
+  /*
+   * we don't have begin lines in Base64 or plain text files.
+   */
+  if (data->uudet == B64ENCODED || data->uudet == QP_ENCODED ||
+      data->uudet == PT_ENCODED)
+    state = DATA;
+
+  /*
+   * If we know that the file does not have a begin, we simulate
+   * it in desperate mode
+   */
+
+  if ((data->state & UUFILE_NOBEGIN) && uu_desperate) {
+    UUMessage (uunconc_id, __LINE__, UUMSG_WARNING, uustring(S_DATA_SUSPICIOUS));
+    state = DATA;
+  }
+
+  (void) UUDecodeLine (NULL, NULL, 0);                   /* init */
+  (void) UUbhwrite    (NULL, 0, 0, NULL);                /* dito */
+  (void) UUDecodePart (NULL, NULL, NULL, 0, 0, 0, NULL); /* yep  */
+
+  /*
+   * initialize progress information
+   */
+  progress.action = 0;
+  if (data->filename != NULL) {
+    _FP_strncpy (progress.curfile,
+		 (strlen(data->filename)>2047)?
+		 (data->filename+strlen(data->filename)-2047):data->filename,
+		 2048);
+  }
+  else {
+    _FP_strncpy (progress.curfile,
+		 (strlen(data->binfile)>2047)?
+		 (data->binfile+strlen(data->binfile)-2047):data->binfile,
+		 2048);
+  }
+  progress.partno   =  0;
+  progress.numparts =  0;
+  progress.fsize    = -1;
+  progress.percent  =  0;
+  progress.action   =  UUACT_DECODING;
+
+  iter = data->thisfile;
+  while (iter) {
+    progress.numparts = (iter->partno)?iter->partno:1;
+    iter = iter->NEXT;
+  }
+
+  /*
+   * let's rock!
+   */
+
+  iter = data->thisfile;
+  while (iter) {
+
+    if (part==-1 && iter->partno!=1)
+       UUMessage (uunconc_id, __LINE__, UUMSG_WARNING, "Missing everything before part #%d", iter->partno);
+    if (part!=-1 && iter->partno!=part+1) {
+      if (!uu_desperate)
+        break;
+      UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
+                 uustring(S_PART_MISSING), part);
+    }
+    part = iter->partno;
+
+    if (iter->data->sfname == NULL) {
+      iter = iter->NEXT;
+      continue;
+    }
+
+    /*
+     * call our FileCallback to retrieve the file
+     */
+
+    if (uu_FileCallback) {
+      if ((res = (*uu_FileCallback) (uu_FileCBArg, iter->data->sfname,
+				     uugen_fnbuffer, 1)) != UURET_OK)
+	break;
+      if ((datain = fopen (uugen_fnbuffer, "rb")) == NULL) {
+	(*uu_FileCallback) (uu_FileCBArg, iter->data->sfname,
+			    uugen_fnbuffer, 0);
+	UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
+		   uustring (S_NOT_OPEN_FILE),
+		   uugen_fnbuffer, strerror (uu_errno = errno));
+	res = UURET_IOERR;
+	break;
+      }
+    }
+    else {
+      if ((datain = fopen (iter->data->sfname, "rb")) == NULL) {
+	UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
+		   uustring (S_NOT_OPEN_FILE),
+		   iter->data->sfname, strerror (uu_errno = errno));
+	res = UURET_IOERR;
+	break;
+      }
+      _FP_strncpy (uugen_fnbuffer, iter->data->sfname, 1024);
+    }
+
+    progress.partno  = part;
+    progress.fsize   = (iter->data->length)?iter->data->length:-1;
+    progress.percent = 0;
+    progress.foffset = iter->data->startpos;
+
+    fseek              (datain, iter->data->startpos, SEEK_SET);
+    res = UUDecodePart (datain, dataout, &state,
+			iter->data->startpos+iter->data->length,
+			data->uudet, iter->data->flags, NULL);
+    fclose             (datain);
+
+    if (uu_FileCallback)
+      (*uu_FileCallback) (uu_FileCBArg, iter->data->sfname, uugen_fnbuffer, 0);
+
+    if (state == DONE || res != UURET_OK)
+      break;
+
+    iter = iter->NEXT;
+  }
+
+  if (state == DATA &&
+      (data->uudet == B64ENCODED || data->uudet == QP_ENCODED ||
+       data->uudet == PT_ENCODED))
+    state = DONE; /* assume we're done */
+
+  if (fclose (dataout)) {
+    UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
+	       uustring (S_WR_ERR_TEMP),
+	       strerror (uu_errno = errno));
+    res = UURET_IOERR;
+  }
+
+  if (res != UURET_OK || (state != DONE && !uu_desperate)) {
+    unlink (data->binfile);
+    _FP_free (data->binfile);
+    data->binfile = NULL;
+    data->state  &= ~UUFILE_TMPFILE;
+    data->state  |=  UUFILE_ERROR;
+
+    if (res == UURET_OK && state != DONE)
+      res = UURET_NOEND;
+  }
+  else if (res != UURET_OK) {
+    data->state &= ~UUFILE_DECODED;
+    data->state |=  UUFILE_ERROR | UUFILE_TMPFILE;
+  }
+  else {
+    data->state &= ~UUFILE_ERROR;
+    data->state |=  UUFILE_TMPFILE;
+  }
+
+  /*
+   * If this was a BinHex file, we must extract its data or resource fork
+   */
+
+  if (data->uudet == BH_ENCODED && data->binfile)
+  {
+    char * template = g_build_filename (g_get_tmp_dir(), "uuXXXXXX", NULL);
+    ntmp = strdup (template);
+    fd = g_mkstemp (ntmp);
+    g_free (template);
+
+    if (fd == -1) {
+      UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
+		 uustring (S_NO_TEMP_NAME));
+      progress.action = 0;
+      free (ntmp);
+      return UURET_NOMEM;
+    }
+    if ((datain = fdopen (fd, "rb")) == NULL) {
+      UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
+		 uustring (S_NOT_OPEN_FILE),
+		 data->binfile, strerror (uu_errno = errno));
+      progress.action = 0;
+      free (ntmp);
+      return UURET_IOERR;
+    }
+    if ((dataout = fopen (ntmp, "wb")) == NULL) {
+      UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
+		 uustring (S_NOT_OPEN_TARGET),
+		 ntmp, strerror (uu_errno = errno));
+      progress.action = 0;
+      fclose (datain);
+      free   (ntmp);
+      return UURET_IOERR;
+    }
+    /*
+     * read fork lengths. remember they're in Motorola format
+     */
+    r[0] = fgetc (datain);
+    hb   = (int) r[0] + 22;
+    fseek (datain, (int) r[0] + 12, SEEK_SET);
+    fread (r, 1, 8, datain);
+
+    dsize = (((long) 1 << 24) * (long) r[0]) +
+            (((long) 1 << 16) * (long) r[1]) +
+            (((long) 1 <<  8) * (long) r[2]) +
+            (                   (long) r[3]);
+    rsize = (((long) 1 << 24) * (long) r[4]) +
+	    (((long) 1 << 16) * (long) r[5]) +
+	    (((long) 1 <<  8) * (long) r[6]) +
+	    (                   (long) r[7]);
+
+    UUMessage (uunconc_id, __LINE__, UUMSG_MESSAGE,
+	       uustring (S_BINHEX_SIZES),
+	       dsize, rsize);
+
+    if (dsize == 0) {
+      fseek  (datain, dsize + hb + 2, SEEK_SET);
+      numbytes = rsize;
+    }
+    else if (rsize == 0) {
+      fseek  (datain, hb, SEEK_SET);
+      numbytes = dsize;
+    }
+    else {
+      /* we should let the user have the choice here */
+      UUMessage (uunconc_id, __LINE__, UUMSG_NOTE,
+		 uustring (S_BINHEX_BOTH));
+      fseek  (datain, hb, SEEK_SET);
+      numbytes = dsize;
+    }
+
+    progress.action   = 0;
+    progress.partno   = 0;
+    progress.numparts = 1;
+    progress.fsize    = (numbytes)?numbytes:-1;
+    progress.foffset  = hb;
+    progress.percent  = 0;
+    progress.action   = UUACT_COPYING;
+
+    /*
+     * copy the chosen fork
+     */
+
+    while (!feof (datain) && numbytes) {
+      if (UUBUSYPOLL(ftell(datain)-progress.foffset,progress.fsize)) {
+	UUMessage (uunconc_id, __LINE__, UUMSG_NOTE,
+		   uustring (S_DECODE_CANCEL));
+	fclose (datain);
+	fclose (dataout);
+	unlink (ntmp);
+	free   (ntmp);
+	return UURET_CANCEL;
+      }
+
+      bytes = fread (uugen_inbuffer, 1,
+		     (size_t) ((numbytes>1024)?1024:numbytes), datain);
+
+      if (ferror (datain) || (bytes == 0 && !feof (datain))) {
+	progress.action = 0;
+	UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
+		   uustring (S_SOURCE_READ_ERR),
+		   data->binfile, strerror (uu_errno = errno));
+	fclose (datain);
+	fclose (dataout);
+	unlink (ntmp);
+	free   (ntmp);
+	return UURET_IOERR;
+      }
+      if (fwrite (uugen_inbuffer, 1, bytes, dataout) != bytes) {
+	progress.action = 0;
+	UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
+		   uustring (S_WR_ERR_TARGET),
+		   ntmp, strerror (uu_errno = errno));
+	fclose (datain);
+	fclose (dataout);
+	unlink (ntmp);
+	free   (ntmp);
+	return UURET_IOERR;
+      }
+      numbytes -= bytes;
+    }
+
+    if (numbytes) {
+      UUMessage (uunconc_id, __LINE__, UUMSG_WARNING,
+		 uustring (S_SHORT_BINHEX),
+		 (data->filename)?data->filename:
+		 (data->subfname)?data->subfname:"???",
+		 numbytes);
+    }
+
+    /*
+     * replace temp file
+     */
+
+    fclose (datain);
+    if (fclose (dataout)) {
+      UUMessage (uunconc_id, __LINE__, UUMSG_ERROR,
+		 uustring (S_WR_ERR_TARGET),
+		 ntmp, strerror (uu_errno = errno));
+      unlink (ntmp);
+      free   (ntmp);
+      return UURET_IOERR;
+    }
+
+    if (unlink (data->binfile)) {
+      UUMessage (uunconc_id, __LINE__, UUMSG_WARNING,
+		 uustring (S_TMP_NOT_REMOVED),
+		 data->binfile, strerror (uu_errno = errno));
+    }
+
+    free (data->binfile);
+    data->binfile = ntmp;
+  }
+
+  progress.action = 0;
+  return res;
+}
+
+/*
+ * QuickDecode for proper MIME attachments. We expect the pointer to
+ * be on the first header line.
+ */
+
+int
+UUQuickDecode (FILE *datain, FILE *dataout, char *boundary, long maxpos)
+{
+  int state=BEGIN, encoding=-1;
+  headers myenv;
+
+  /*
+   * Read header and find out about encoding.
+   */
+
+  memset (&myenv, 0, sizeof (headers));
+  UUScanHeader (datain, &myenv);
+
+  if (_FP_stristr (myenv.ctenc, "uu") != NULL)
+    encoding = UU_ENCODED;
+  else if (_FP_stristr (myenv.ctenc, "xx") != NULL)
+    encoding = XX_ENCODED;
+  else if (_FP_stricmp (myenv.ctenc, "base64") == 0)
+    encoding = B64ENCODED;
+  else if (_FP_stricmp (myenv.ctenc, "quoted-printable") == 0)
+    encoding = QP_ENCODED;
+  else
+    encoding = PT_ENCODED;
+
+  UUkillheaders (&myenv);
+
+  /*
+   * okay, so decode this one
+   */
+
+  (void) UUDecodePart (NULL, NULL, NULL, 0, 0, 0, NULL); /* init  */
+  return UUDecodePart (datain, dataout, &state, maxpos,
+		       encoding, FL_PROPER|FL_TOEND,
+		       boundary);
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]