summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Frysinger <vapier@gentoo.org>2003-09-13 03:46:59 +0000
committerMike Frysinger <vapier@gentoo.org>2003-09-13 03:46:59 +0000
commitc06f32cde6f99a539fc177ccf1f2680d89468f2a (patch)
tree05413012831e5a3f2520279e95fc42436dadf11d /media-sound/xmms
parentwhite space fixes (diff)
downloadgentoo-2-c06f32cde6f99a539fc177ccf1f2680d89468f2a.tar.gz
gentoo-2-c06f32cde6f99a539fc177ccf1f2680d89468f2a.tar.bz2
gentoo-2-c06f32cde6f99a539fc177ccf1f2680d89468f2a.zip
DTD patch #27967
Diffstat (limited to 'media-sound/xmms')
-rw-r--r--media-sound/xmms/ChangeLog8
-rw-r--r--media-sound/xmms/files/digest-xmms-1.2.8-r12
-rw-r--r--media-sound/xmms/files/xmms-1.2.8-dtd.patch1985
-rw-r--r--media-sound/xmms/files/xmms-1.2.8-jump.patch76
-rw-r--r--media-sound/xmms/files/xmms-1.2.8-russian-charset.patch2174
-rw-r--r--media-sound/xmms/files/xmms-1.2.8-sigterm.patch31
-rw-r--r--media-sound/xmms/xmms-1.2.8-r1.ebuild139
7 files changed, 4414 insertions, 1 deletions
diff --git a/media-sound/xmms/ChangeLog b/media-sound/xmms/ChangeLog
index b8bfe1d87efb..09ec24933832 100644
--- a/media-sound/xmms/ChangeLog
+++ b/media-sound/xmms/ChangeLog
@@ -1,6 +1,12 @@
# ChangeLog for media-sound/xmms
# Copyright 2002-2003 Gentoo Technologies, Inc.; Distributed under the GPL v2
-# $Header: /var/cvsroot/gentoo-x86/media-sound/xmms/ChangeLog,v 1.66 2003/09/12 19:27:39 seemant Exp $
+# $Header: /var/cvsroot/gentoo-x86/media-sound/xmms/ChangeLog,v 1.67 2003/09/13 03:46:59 vapier Exp $
+
+*xmms-1.2.8-r1 (12 Sep 2003)
+
+ 12 Sep 2003; Mike Frysinger <vapier@gentoo.org> :
+ Added Dynamic Taste Detection #27967 and removed some of the elibtoolize
+ and autotools crud.
*xmms-1.2.8 (12 Sep 2003)
diff --git a/media-sound/xmms/files/digest-xmms-1.2.8-r1 b/media-sound/xmms/files/digest-xmms-1.2.8-r1
new file mode 100644
index 000000000000..55451e9e64ea
--- /dev/null
+++ b/media-sound/xmms/files/digest-xmms-1.2.8-r1
@@ -0,0 +1,2 @@
+MD5 bf1d7ea568b01d72e99d75ae9d0d1594 xmms-1.2.8.tar.gz 3884444
+MD5 a68edd5b03d650c366f09852a8414ee2 gentoo_ice.zip 68458
diff --git a/media-sound/xmms/files/xmms-1.2.8-dtd.patch b/media-sound/xmms/files/xmms-1.2.8-dtd.patch
new file mode 100644
index 000000000000..94b342568b0c
--- /dev/null
+++ b/media-sound/xmms/files/xmms-1.2.8-dtd.patch
@@ -0,0 +1,1985 @@
+diff -ruN dtd-old/AUTHORS dtd-new/AUTHORS
+--- dtd-old/AUTHORS 2003-01-07 15:50:08.000000000 +0100
++++ dtd-new/AUTHORS 2003-09-08 13:46:19.000000000 +0200
+@@ -42,6 +42,7 @@
+ Chris Wilson
+ Dave Yearke
+ Stephan K. Zitz
++ Johan Walles (Dynamic Taste Detection)
+
+ Default skin: Leonard "Blayde" Tan
+ Robin Sylvestre (Equalizer and Playlist)
+diff -ruN dtd-old/README dtd-new/README
+--- dtd-old/README 2003-09-04 23:00:18.000000000 +0200
++++ dtd-new/README 2003-09-08 13:46:19.000000000 +0200
+@@ -57,6 +57,7 @@
+ 5. Features
+ 5.1 Supported File formats
+ 5.2 Supported Features
++ 5.2.1 Dynamic Taste Detection
+ 6. Obtaining XMMS
+ 7. Misc
+ 7.1 Shoutcast support
+@@ -1121,6 +1122,34 @@
+ Compiles and works on other UNIX's
+ Proxy authentication support
+
++5.2.1 Dynamic Taste Detection
++-----------------------------
++Basically, Dynamic Taste Detection collects statistics on how you
++listen to songs, and then adapts the shuffle play and randomize
++playlist functions to better follow your taste. It takes care of
++itself entirely, so that's all you really need to know about it ;-).
++
++For those of you who are curious, here is how it works. Two kinds of
++statistics are collected:
++
++- What songs you don't like
++- What songs you like to hear next to each other
++
++The first one is easy. When you skip a song, it looses a point.
++Songs with low scores are placed at the end of the playlist.
++
++What songs you want to hear next to each other are a bit more
++intricate. Let's say you have two songs, A and B. A (or any song
++above it in the playlist) is playing. You move song B to the position
++right after A. A finishes playing, and B starts. This is interpreted
++as "you want to hear A and B next to each other".
++
++In this example, A and B will then *tend* to (i.e. not always) end up
++next to each other in the playlist (though not necessarily in that
++order).
++
++To explicitly change your opinion of a song, or disconnect one song
++from another, you can do so from the right-click menu in the playlist.
+
+ 6. Obtaining XMMS
+ -------------------
+diff -ruN dtd-old/xmms/Makefile.am dtd-new/xmms/Makefile.am
+--- dtd-old/xmms/Makefile.am 2003-09-08 13:31:40.000000000 +0200
++++ dtd-new/xmms/Makefile.am 2003-09-08 13:46:19.000000000 +0200
+@@ -34,6 +34,7 @@
+ menurow.c menurow.h \
+ hslider.c hslider.h \
+ monostereo.c monostereo.h \
++dtd.c dtd.h \
+ vis.c vis.h \
+ svis.c svis.h \
+ number.c number.h \
+diff -ruN dtd-old/xmms/dtd.c dtd-new/xmms/dtd.c
+--- dtd-old/xmms/dtd.c 1970-01-01 01:00:00.000000000 +0100
++++ dtd-new/xmms/dtd.c 2003-09-08 14:37:21.000000000 +0200
+@@ -0,0 +1,624 @@
++/* XMMS - Cross-platform multimedia player
++ * Copyright (C) 2001 Johan Walles, d92-jwa@nada.kth.se
++ *
++ * 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.
++ *
++ * 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.
++ */
++
++/* Hint: DTD = Dynamic Taste Detection */
++
++#include <assert.h>
++
++#include "xmms.h"
++
++static GList *dtd_recommendations_list;
++
++static DtdNextSong *dtd_find_next_song(DtdStartSong *start_song,
++ const gchar *filename)
++{
++ GList *iterator;
++
++ for (iterator = start_song->next_song;
++ iterator != NULL;
++ iterator = g_list_next(iterator))
++ {
++ if (strcmp(((DtdNextSong *)(iterator->data))->filename,
++ filename) == 0)
++ {
++ return (DtdNextSong *)(iterator->data);
++ }
++ }
++
++ return NULL;
++}
++
++static DtdStartSong *dtd_find_start_song(const gchar *filename)
++{
++ GList *iterator;
++
++ if (filename == NULL)
++ {
++ return NULL;
++ }
++
++ for (iterator = dtd_recommendations_list;
++ iterator != NULL;
++ iterator = g_list_next(iterator))
++ {
++ if (strcmp(((DtdStartSong *)(iterator->data))->filename,
++ filename) == 0)
++ {
++ return (DtdStartSong *)(iterator->data);
++ }
++ }
++
++ return NULL;
++}
++
++/*
++ * Memorize that after playing the file named start, the user might
++ * want to hear the file named follow.
++ */
++static void __dtd_recommend_next(int weight, const gchar *start, const gchar *follow)
++{
++ DtdStartSong *start_song;
++ DtdNextSong *next_song;
++
++ /* Check if the start song already has recommendations. */
++ if ((start_song = dtd_find_start_song(start)) ==
++ NULL)
++ {
++ /* Nope, create a new recommendation start node */
++ start_song = g_new(DtdStartSong, 1);
++
++ start_song->filename = g_strdup(start);
++ start_song->next_song = NULL;
++ start_song->score = 0;
++
++ /* Add the recommendation start node to the
++ recommendation start node list. */
++ dtd_recommendations_list =
++ g_list_append(dtd_recommendations_list,
++ start_song);
++ }
++
++ /* Check if the next song is already recommended for this
++ start song */
++ if ((next_song = dtd_find_next_song(start_song,
++ follow)) == NULL)
++ {
++ /* It's not. Create a recommendation next node */
++ next_song = g_new(DtdNextSong, 1);
++
++ next_song->filename = g_strdup(follow);
++
++ next_song->weight = 0;
++
++ /* Add the new recommendation to the start node */
++ start_song->next_song =
++ g_list_append(start_song->next_song,
++ next_song);
++ }
++
++ next_song->weight += weight;
++}
++
++/*
++ * Memorize that after playing the file named start, the user might
++ * want to hear the file named follow.
++ */
++void dtd_recommend_next(const gchar *start, const gchar *follow)
++{
++ /* Recommend symmetrically */
++ __dtd_recommend_next(1, start, follow);
++ __dtd_recommend_next(1, follow, start);
++}
++
++void __dtd_dissociate(const gchar *file1, const gchar *file2)
++{
++ DtdStartSong *startSong;
++ DtdNextSong *nextSong = NULL;
++ GList *nextSongIterator;
++
++ // Find the start pointer for file1
++ startSong = dtd_find_start_song(file1);
++ if (startSong == NULL)
++ {
++ // No such recommendation exists
++ return;
++ }
++
++ // Find its next pointer for file2
++ for (nextSongIterator = startSong->next_song;
++ nextSongIterator != NULL;
++ nextSongIterator = g_list_next(nextSongIterator))
++ {
++ nextSong = (DtdNextSong *)(nextSongIterator->data);
++ if (strcmp(file2, nextSong->filename) == 0)
++ {
++ break;
++ }
++ }
++ if (nextSong == NULL)
++ {
++ // No such recommendation exists
++ return;
++ }
++
++ // Remove the next pointer
++ startSong->next_song =
++ g_list_remove_link(startSong->next_song, nextSongIterator);
++ g_free(nextSong->filename);
++ g_free(nextSong);
++ g_list_free_1(nextSongIterator);
++}
++
++void dtd_dissociate(const gchar *file1, const gchar *file2)
++{
++ // The slave's chain is heavy in both ends, so let's set 'em
++ // both free :-)
++ __dtd_dissociate(file1, file2);
++ __dtd_dissociate(file2, file1);
++}
++
++/*
++ * Modify a song's score.
++ */
++void dtd_set_score(const gchar *filename, gint score)
++{
++ DtdStartSong *start_song = dtd_find_start_song(filename);
++
++ if (start_song == NULL)
++ {
++ start_song = g_new(DtdStartSong, 1);
++
++ start_song->filename = g_strdup(filename);
++ start_song->next_song = NULL;
++ start_song->score = 0;
++
++ /* Add the new recommendation start node to the
++ recommendation start node list. */
++ dtd_recommendations_list =
++ g_list_append(dtd_recommendations_list,
++ start_song);
++ }
++
++ start_song->score = score;
++ /* Scores > 0 means "always put this song at the start of the
++ list". Probably we don't want that.
++ */
++ if (start_song->score > 0)
++ {
++ start_song->score = 0;
++ }
++}
++
++void dtd_change_score(const gchar *filename, gint delta)
++{
++ DtdStartSong *start_song = dtd_find_start_song(filename);
++
++ if (start_song == NULL)
++ {
++ start_song = g_new(DtdStartSong, 1);
++
++ start_song->filename = g_strdup(filename);
++ start_song->next_song = NULL;
++ start_song->score = 0;
++
++ /* Add the new recommendation start node to the
++ recommendation start node list. */
++ dtd_recommendations_list =
++ g_list_append(dtd_recommendations_list,
++ start_song);
++ }
++
++ /* Scores > 0 means "always put this song at the start of the
++ list". Probably we don't want that.
++ */
++ start_song->score += delta;
++ if (start_song->score > 0)
++ {
++ start_song->score = 0;
++ }
++}
++
++/*
++ * Retrieve a song's score.
++ */
++gint dtd_get_score(const gchar *filename)
++{
++ DtdStartSong *start_song = dtd_find_start_song(filename);
++
++ if (start_song == NULL)
++ {
++ return 0;
++ }
++ else
++ {
++ return start_song->score;
++ }
++}
++
++const GList *dtd_get_recommendations(const gchar *start)
++{
++ DtdStartSong *dtdStartSong = dtd_find_start_song(start);
++
++ return dtdStartSong ? dtdStartSong->next_song : NULL;
++}
++
++static void __dtd_set_score(gchar *filename, gint score)
++{
++ DtdStartSong *start_song = dtd_find_start_song(filename);
++
++ if (start_song == NULL)
++ {
++ start_song = g_new(DtdStartSong, 1);
++
++ start_song->filename = g_strdup(filename);
++ start_song->next_song = NULL;
++ start_song->score = 0;
++
++ /* Add the new recommendation start node to the
++ recommendation start node list. */
++ dtd_recommendations_list =
++ g_list_append(dtd_recommendations_list,
++ start_song);
++ }
++
++ start_song->score = score;
++}
++
++static guint dtd_sum_recommendations(DtdStartSong *start_song)
++{
++ GList *iterator;
++ guint sum = 0;
++
++ assert(start_song != NULL);
++
++ for (iterator = start_song->next_song;
++ iterator != NULL;
++ iterator = g_list_next(iterator))
++ {
++ sum += ((DtdNextSong *)(iterator->data))->weight;
++ }
++
++ assert(sum != 0);
++
++ return sum;
++}
++
++static const gchar *dtd_get_nth_recommendation(DtdStartSong *start_song,
++ guint n)
++{
++ GList *iterator;
++ guint sum = 0;
++
++ assert(start_song != NULL);
++
++ if (n == 0)
++ return NULL;
++
++ for (iterator = start_song->next_song;
++ iterator != NULL;
++ iterator = g_list_next(iterator))
++ {
++ sum += ((DtdNextSong *)(iterator->data))->weight;
++
++ if (sum >= n)
++ return ((DtdNextSong *)(iterator->data))->filename;
++ }
++
++ /* If we get here then the n passed to this function was too
++ large. It can be no larger than the number calculated by
++ dtd_sum_recommendations(). */
++ assert(FALSE);
++
++ return NULL;
++}
++
++/*
++ Try to recommend a song to play after first_filename. NULL means
++ that no recommendation is given (for whatever reason).
++
++ FIXME: This method should accept a list of songs *not* to choose
++ between. If the user has asked for a recommendation and received an
++ answer that is not in the current playlist, they must be able to try
++ again.
++*/
++const gchar *dtd_get_recommendation(const gchar *first_filename)
++{
++ DtdStartSong *start_song;
++ guint recommendation_total;
++
++ if (first_filename == NULL)
++ {
++ return NULL;
++ }
++
++ if ((start_song = dtd_find_start_song(first_filename)) ==
++ NULL)
++ {
++ /* There are no recommendations for first_filename */
++ return NULL;
++ }
++
++ if (start_song->next_song == NULL)
++ {
++ return NULL;
++ }
++
++ recommendation_total = dtd_sum_recommendations(start_song);
++
++ return dtd_get_nth_recommendation(start_song,
++ rand() % (recommendation_total + 1));
++}
++
++static void dtd_clear(void)
++{
++ GList *start_iterator;
++ GList *next_iterator;
++
++ if (dtd_recommendations_list == NULL)
++ {
++ return;
++ }
++
++ /* Free all memory used by the recommendations */
++ for (start_iterator = dtd_recommendations_list;
++ start_iterator != NULL;
++ start_iterator = g_list_next(start_iterator))
++ {
++ /* Free all file names in the next list */
++ for (next_iterator = ((DtdStartSong *)(start_iterator->data))->next_song;
++ next_iterator != NULL;
++ next_iterator = g_list_next(next_iterator))
++ {
++ free(((DtdNextSong *)(next_iterator->data))->filename);
++ ((DtdNextSong *)(next_iterator->data))->filename = NULL;
++
++ free(next_iterator->data);
++ next_iterator->data = NULL;
++ }
++ /* Free the next list */
++ g_list_free(((DtdStartSong *)(start_iterator->data))->next_song);
++ ((DtdStartSong *)(start_iterator->data))->next_song = NULL;
++
++ /* Free the file name */
++ free(((DtdStartSong *)(start_iterator->data))->filename);
++ ((DtdStartSong *)(start_iterator->data))->filename = NULL;
++
++ /* Free the struct */
++ free(start_iterator->data);
++ start_iterator->data = NULL;
++ }
++ g_list_free(dtd_recommendations_list);
++ dtd_recommendations_list = NULL;
++}
++
++void dtd_init(void)
++{
++ gchar *dtd_data_file_name;
++ FILE *dtd_data_file;
++
++ gchar start[950], follow[950];
++ gchar line[999];
++ int weight;
++
++ enum { NEXT, SCORES } section = NEXT;
++
++ gboolean failure = FALSE;
++
++ /* We have no recommendations to begin with */
++ dtd_recommendations_list = NULL;
++
++ /* Make up a filename for the dtd data file */
++ dtd_data_file_name = g_strconcat(g_get_home_dir(),
++ "/.xmms/",
++ DTD_DATA_FILE_NAME,
++ NULL);
++
++ /* Open the dtd data file for input */
++ dtd_data_file = fopen(dtd_data_file_name,
++ "r");
++ if (dtd_data_file == NULL)
++ {
++ return;
++ }
++
++ /* Parse the dtd data file */
++ start[0] = '\0';
++ while (!feof(dtd_data_file))
++ {
++ if (fgets(line, 990, dtd_data_file) != NULL)
++ {
++ if (sscanf(line, "%d %940[^\n]\n", &weight, follow) == 2)
++ {
++ /* Found a line with both number and name */
++
++ switch (section)
++ {
++ case NEXT:
++ if (start[0] != '\0')
++ {
++ if (weight < 0)
++ {
++ failure= TRUE;
++ }
++ else if (weight > 0)
++ {
++ __dtd_recommend_next(weight, start, follow);
++ }
++ }
++ else
++ {
++ /* Parse error! */
++ failure = TRUE;
++ }
++ break;
++
++ case SCORES:
++ if (weight != 0)
++ {
++ __dtd_set_score(follow, weight);
++ }
++ break;
++ }
++
++ if (failure) break;
++ }
++ else if (sscanf(line, "%940[^\n]\n", start) == 1)
++ {
++ if (strcmp(start, "scores") == 0)
++ {
++ /* The scores section starts here */
++ section = SCORES;
++ start[0] = '\0';
++ }
++ else
++ {
++ /* New start song read OK */
++ section = NEXT;
++ }
++ }
++ else
++ {
++ /* Parse error! */
++ failure = TRUE;
++ break;
++ }
++ }
++ }
++
++ if (failure)
++ {
++ /* Free all space occupied by the recommendations list
++ in favour of remembering a broken list. */
++ dtd_clear();
++ }
++
++ fclose(dtd_data_file);
++ dtd_data_file = NULL;
++}
++
++void dtd_persist(void)
++{
++ gchar *dtd_data_file_name;
++ FILE *dtd_data_file;
++
++ GList *start_iterator;
++ GList *next_iterator;
++
++ gboolean failure = FALSE;
++
++ /* Are there any recommendations to store */
++ if (dtd_recommendations_list == NULL)
++ {
++ /* Nope. */
++ return;
++ }
++
++ /* Make up a filename for the dtd data file */
++ dtd_data_file_name = g_strconcat(g_get_home_dir(),
++ "/.xmms/",
++ DTD_DATA_FILE_NAME,
++ NULL);
++
++ /* Open the dtd data file for output */
++ dtd_data_file = fopen(dtd_data_file_name,
++ "w");
++ if (dtd_data_file == NULL)
++ {
++ /* FIXME: We fail silently. Should we output a
++ * warning somehow? */
++ return;
++ }
++
++ /* Loop through all recommendation starts, storing their
++ recommended followers */
++ for (start_iterator = dtd_recommendations_list;
++ start_iterator != NULL;
++ start_iterator = g_list_next(start_iterator))
++ {
++ if (((DtdStartSong *)(start_iterator->data))->next_song == NULL)
++ {
++ /* Don't store songs with only a score (yet). */
++ continue;
++ }
++
++ if (failure ||
++ (fprintf(dtd_data_file,
++ "%s\n",
++ ((DtdStartSong *)(start_iterator->data))->filename) == 0))
++ {
++ failure = TRUE;
++ break;
++ }
++
++ for (next_iterator = ((DtdStartSong *)(start_iterator->data))->next_song;
++ next_iterator != NULL;
++ next_iterator = g_list_next(next_iterator))
++ {
++ if (fprintf(dtd_data_file, "%d %s\n",
++ ((DtdNextSong *)(next_iterator->data))->weight,
++ ((DtdNextSong *)(next_iterator->data))->filename) == 0)
++ {
++ failure = TRUE;
++ break;
++ }
++ }
++ }
++
++ if (!failure)
++ {
++ /* Output the "here starts the scoring section" marker. */
++ if (fprintf(dtd_data_file, "scores\n") == 0)
++ {
++ failure = TRUE;
++ }
++
++ /* Output all (non-zero) scores */
++ for (start_iterator = dtd_recommendations_list;
++ start_iterator != NULL;
++ start_iterator = g_list_next(start_iterator))
++ {
++ if (((DtdStartSong *)(start_iterator->data))->score == 0)
++ {
++ /* Don't store songs with zero score. */
++ continue;
++ }
++
++ if (failure ||
++ (fprintf(dtd_data_file,
++ "%d %s\n",
++ ((DtdStartSong *)(start_iterator->data))->score,
++ ((DtdStartSong *)(start_iterator->data))->filename) == 0))
++ {
++ failure = TRUE;
++ break;
++ }
++ }
++ }
++
++ fclose(dtd_data_file);
++ dtd_data_file = NULL;
++
++ if (failure)
++ {
++ /* If writing of the data file failed, attempt to
++ remove it rather than leaving a broken data file
++ behind. */
++
++ unlink(dtd_data_file_name);
++ }
++}
+diff -ruN dtd-old/xmms/dtd.h dtd-new/xmms/dtd.h
+--- dtd-old/xmms/dtd.h 1970-01-01 01:00:00.000000000 +0100
++++ dtd-new/xmms/dtd.h 2003-09-08 14:38:32.000000000 +0200
+@@ -0,0 +1,73 @@
++/* XMMS - Cross-platform multimedia player
++ * Copyright (C) 2001 Johan Walles, d92-jwa@nada.kth.se
++ *
++ * 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.
++ *
++ * 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.
++ */
++
++/* Hint: DTD = Dynamic Taste Detection */
++
++/* FIXME: Should lists prove to be too slow we could use balanced
++ trees for the DtdStartSong collection. The DtdNextSong is scanned
++ through however, so tree-ifying will do no good. */
++
++#ifndef DTD_H
++#define DTD_H
++
++#define DTD_DATA_FILE_NAME "dtd-stats"
++
++typedef struct
++{
++ gchar *filename;
++ guint weight;
++}
++DtdNextSong;
++
++typedef struct
++{
++ gchar *filename;
++ gint score;
++ GList *next_song;
++}
++DtdStartSong;
++
++void dtd_init(void);
++void dtd_persist(void);
++
++// Tell DTD that after start, the user tends to want to hear follow
++void dtd_recommend_next(const gchar *start,
++ const gchar *follow);
++
++// Tell DTD to break the association between file1 and file2 (if one
++// exists)
++void dtd_dissociate(const gchar *file1,
++ const gchar *file2);
++
++
++// Let DTD recommend what to play after start. NULL = No
++// recommendation = anything goes.
++const gchar *dtd_get_recommendation(const gchar *start);
++
++// Change the absolute score for a file
++void dtd_change_score(const gchar *filename, gint delta);
++void dtd_set_score(const gchar *filename, gint score);
++
++// Get the score for a file. Unknown file = score 0.
++gint dtd_get_score(const gchar *filename);
++
++// Get the list of recommendations for a given file. NULL = no
++// recommendations for that file.
++const GList *dtd_get_recommendations(const gchar *start);
++
++#endif
+diff -ruN dtd-old/xmms/main.c dtd-new/xmms/main.c
+--- dtd-old/xmms/main.c 2003-09-08 13:31:40.000000000 +0200
++++ dtd-new/xmms/main.c 2003-09-08 13:49:16.000000000 +0200
+@@ -476,6 +476,8 @@
+ }
+ }
+ xmms_cfg_read_string(cfgfile, "xmms", "generic_title_format", &cfg.gentitle_format);
++
++ xmms_cfg_read_boolean(cfgfile, "xmms", "dynamic_taste_detection", &cfg.dynamic_taste_detection);
+
+ xmms_cfg_free(cfgfile);
+ }
+@@ -674,6 +676,8 @@
+ g_free(str);
+ }
+ xmms_cfg_write_string(cfgfile, "xmms", "generic_title_format", cfg.gentitle_format);
++
++ xmms_cfg_write_boolean(cfgfile, "xmms", "dynamic_taste_detection", cfg.dynamic_taste_detection);
+
+ xmms_cfg_write_file(cfgfile, filename);
+ xmms_cfg_free(cfgfile);
+@@ -903,6 +907,7 @@
+ playlist_clear();
+ cleanup_plugins();
+ sm_cleanup();
++ dtd_persist();
+ gtk_exit(0);
+ }
+
+@@ -3130,6 +3135,7 @@
+ GDK_THREADS_LEAVE();
+ }
+
++ cfg.dynamic_taste_detection = TRUE;
+
+ GDK_THREADS_ENTER();
+ check_ctrlsocket();
+@@ -3608,6 +3614,10 @@
+ mainwin_timeout_tag = gtk_timeout_add(10, idle_func, NULL);
+ playlist_start_get_info_thread();
+
++
++ /* Initialize dynamic taste detection */
++ dtd_init();
++
+ enable_x11r5_session_management(argc, argv);
+ sm_init(argc, argv, options.previous_session_id);
+ gtk_main();
+diff -ruN dtd-old/xmms/main.h dtd-new/xmms/main.h
+--- dtd-old/xmms/main.h 2003-09-08 13:31:40.000000000 +0200
++++ dtd-new/xmms/main.h 2003-09-08 13:51:48.000000000 +0200
+@@ -64,6 +64,7 @@
+ gchar *charset_id3, *charset_output, *charset_fs, *charset_pl;
+ gint autocharset;
+ gchar *shade_font;
++ gboolean dynamic_taste_detection;
+ }
+ Config;
+
+diff -ruN dtd-old/xmms/playlist.c dtd-new/xmms/playlist.c
+--- dtd-old/xmms/playlist.c 2003-09-08 13:31:40.000000000 +0200
++++ dtd-new/xmms/playlist.c 2003-09-08 14:12:58.000000000 +0200
+@@ -23,6 +23,7 @@
+ #include "charset.h"
+ #include <sys/stat.h>
+ #include <unistd.h>
++#include <assert.h>
+
+ GList *playlist = NULL;
+ GList *shuffle_list = NULL;
+@@ -653,6 +654,17 @@
+
+ plist_pos_list = find_playlist_position_list();
+
++ if ((plist_pos_list != NULL) &&
++ (cfg.repeat || (g_list_next(plist_pos_list) != NULL)))
++ {
++ /*
++ The user is skipping a song. We interpret this as
++ meaning "I don't like this song".
++ */
++ dtd_change_score(((PlaylistEntry *)(plist_pos_list->data))->filename,
++ -1);
++ }
++
+ if (!cfg.repeat && !g_list_next(plist_pos_list))
+ {
+ PL_UNLOCK();
+@@ -725,7 +737,18 @@
+
+ plist_pos_list = find_playlist_position_list();
+ if (g_list_previous(plist_pos_list))
+- playlist_position = plist_pos_list->prev->data;
++ {
++ playlist_position =
++ g_list_previous(plist_pos_list)->data;
++
++ /*
++ The user has gone back to a song. We interpret that
++ as meaning "I like this song so much that I want to
++ hear it again".
++ */
++ dtd_change_score(playlist_position->filename,
++ 1);
++ }
+ else if (cfg.repeat)
+ {
+ GList *node;
+@@ -740,7 +763,7 @@
+ }
+ PL_UNLOCK();
+ playlist_check_pos_current();
+-
++
+ if (restart_playing)
+ playlist_play();
+ else
+@@ -856,6 +879,7 @@
+
+ playlist_position = node->data;
+ PL_UNLOCK();
++ ((PlaylistEntry *)playlist_position)->moved = FALSE;
+ playlist_check_pos_current();
+
+ if (restart_playing)
+@@ -875,12 +899,19 @@
+
+ void playlist_eof_reached(void)
+ {
++ /*
++ * This function is called whenever a song ends and the next
++ * one should start playing.
++ */
++
+ GList *plist_pos_list;
++ GList *previous_plist_pos;
+
+ input_stop();
+
+ PL_LOCK();
+ plist_pos_list = find_playlist_position_list();
++ previous_plist_pos = plist_pos_list->data;
+
+ if (cfg.no_playlist_advance)
+ {
+@@ -912,6 +943,25 @@
+ else
+ playlist_position = plist_pos_list->next->data;
+ PL_UNLOCK();
++
++ if (!cfg.shuffle && (((PlaylistEntry *)playlist_position)->moved))
++ {
++ /*
++ * Song A has stopped playing. Song B will start
++ * playing now. Song B had moved. Thus, we conclude
++ * that the user wants to hear Song B after song A.
++ * Therefore, Song B should be added to Song A's
++ * next-song-preferences.
++ *
++ * Song A is previous_plist_pos. Song B is
++ * playlist_position. */
++
++ dtd_recommend_next(((PlaylistEntry *)previous_plist_pos)->filename,
++ ((PlaylistEntry *)playlist_position)->filename);
++
++ ((PlaylistEntry *)playlist_position)->moved = FALSE;
++ }
++
+ playlist_check_pos_current();
+ playlist_play();
+ mainwin_set_info_text();
+@@ -1146,6 +1196,7 @@
+ ext = strrchr(filename, '.');
+ if (ext && !strcasecmp(ext, ".pls"))
+ {
++ /* It's a playlist, let's parse it. */
+ int noe, i;
+ char key[10];
+
+@@ -1283,6 +1334,11 @@
+ char *ret;
+ PlaylistEntry *entry;
+ GList *node;
++
++ if (pos < 0)
++ {
++ return NULL;
++ }
+
+ PL_LOCK();
+ if (!playlist)
+@@ -1290,6 +1346,11 @@
+ PL_UNLOCK();
+ return NULL;
+ }
++ if (pos >= __get_playlist_length())
++ {
++ PL_UNLOCK();
++ return NULL;
++ }
+ node = g_list_nth(playlist, pos);
+ if (!node)
+ {
+@@ -1355,6 +1416,84 @@
+ return title;
+ }
+
++int playlist_get_score(gint pos)
++{
++ PlaylistEntry *entry;
++ GList *node;
++ gint score;
++
++ PL_LOCK();
++ if (!playlist)
++ {
++ PL_UNLOCK();
++ return 0;
++ }
++ node = g_list_nth(playlist, pos);
++ if (!node)
++ {
++ PL_UNLOCK();
++ return 0;
++ }
++ entry = node->data;
++ score = dtd_get_score(entry->filename);
++ PL_UNLOCK();
++
++ return score;
++}
++
++void playlist_set_score(gint pos, gint score)
++{
++ PlaylistEntry *entry;
++ GList *node;
++
++ PL_LOCK();
++ if (!playlist)
++ {
++ PL_UNLOCK();
++ return;
++ }
++ node = g_list_nth(playlist, pos);
++ if (!node)
++ {
++ PL_UNLOCK();
++ return;
++ }
++ entry = node->data;
++ dtd_set_score(entry->filename, score);
++ PL_UNLOCK();
++}
++
++const gchar* playlist_filename2songtitle(const gchar *filename)
++{
++ GList *iterator;
++
++ if (filename == NULL)
++ {
++ return NULL;
++ }
++
++ // Scan loaded songs for filename
++ PL_LOCK();
++ for (iterator = get_playlist();
++ iterator != NULL;
++ iterator = g_list_next(iterator))
++ {
++ PlaylistEntry *currentEntry =
++ (PlaylistEntry *)(iterator->data);
++
++ if (strcmp(currentEntry->filename, filename) == 0)
++ {
++ // FIXME: Race condition
++ PL_UNLOCK();
++ return currentEntry->title;
++ }
++ }
++ PL_UNLOCK();
++
++ // Not found / don't know
++ return NULL;
++}
++
+ gint playlist_get_songtime(gint pos)
+ {
+ int retval = -1;
+@@ -1616,6 +1755,26 @@
+ PL_UNLOCK();
+ }
+
++static guint playlist_filename_to_index(PlaylistEntry *playlist_entries[],
++ guint n_entries,
++ const gchar *filename)
++{
++ guint i;
++
++ for (i = 0; i < n_entries; i++)
++ {
++ if (strcmp(playlist_entries[i]->filename,
++ filename) == 0)
++ {
++ return i;
++ }
++ }
++
++ return -1;
++}
++
++/* This function is used by the qsort() call in
++ smart_playlist_shuffle_list() (below). */
+ void playlist_sort_selected_by_date(void)
+ {
+ PL_LOCK();
+@@ -1630,7 +1789,20 @@
+ PL_UNLOCK();
+ }
+
+-static GList *playlist_shuffle_list(GList *list)
++static int playlist_entry_score_comparator(const void *a,
++ const void *b)
++{
++
++ int score_a = dtd_get_score((*((PlaylistEntry **)a))->filename);
++ int score_b = dtd_get_score((*((PlaylistEntry **)b))->filename);
++
++ /* We want high scores before low, so if score_a > score_b we
++ want to return something negative. */
++
++ return score_b - score_a;
++}
++
++static GList *smart_playlist_shuffle_list(GList *list)
+ {
+ /* Caller should hold playlist mutex */
+ /*
+@@ -1639,43 +1811,213 @@
+ * fuction is run.
+ */
+ int len = g_list_length(list);
+- int i, j;
+- GList *node, **ptrs;
++ gint i;
++ gint next_score_section = -1;
++ GList *iterator;
++ PlaylistEntry **ptrs;
+
+- if (!len)
++ if (len == 0)
+ return NULL;
+
+- ptrs = g_new(GList *, len);
++ ptrs = g_new(PlaylistEntry *, len);
+
+- for (node = list, i = 0; i < len; node = g_list_next(node), i++)
+- ptrs[i] = node;
++ /* Convert the list into an array of pointers */
++ for (iterator = list, i = 0; i < len; iterator = g_list_next(iterator), i++)
++ {
++ ptrs[i] = (PlaylistEntry *)(iterator->data);
++
++ // Shuffling songs voids information about user listening preferences
++ ptrs[i]->moved = FALSE;
++ }
++ g_list_free(list);
++ list = NULL;
++
++ /* Sort the array by score */
++ qsort(ptrs, len, sizeof(PlaylistEntry *), playlist_entry_score_comparator);
++
++ /* Shuffle the pointer array */
++ for (i = 0; i < len; i++)
++ {
++ PlaylistEntry *swap_ptr;
++ gint j;
++
++ const gchar *previous_filename;
++ const gchar *new_filename;
++
++ if (i >= next_score_section)
++ {
++ /* We are in a new score section; find out
++ where the next one starts */
++ gint current_score =
++ dtd_get_score(ptrs[i]->filename);
++
++ for (next_score_section = i;
++ next_score_section < len;
++ next_score_section++)
++ {
++ if (dtd_get_score(ptrs[next_score_section]->filename) !=
++ current_score)
++ {
++ break;
++ }
++ }
++ }
++
++ if (i == 0)
++ {
++ previous_filename = NULL;
++ }
++ else
++ {
++ previous_filename = ptrs[i - 1]->filename;
++ }
++
++ /* Find out from which index we should get the next song */
++ do {
++ if ((new_filename = dtd_get_recommendation(previous_filename)) !=
++ NULL)
++ {
++ j = playlist_filename_to_index(&(ptrs[i]),
++ len - i,
++ new_filename);
++ if (j != -1)
++ j += i;
++ }
++ else
++ {
++ /* Only find new songs in our current
++ score section */
++ j = (rand() % (next_score_section - i)) + i;
++
++ assert(j < next_score_section);
++ }
++ } while (j == -1);
++
++ assert(j < len);
++ assert(j >= i);
++
++ if (j < next_score_section)
++ {
++ /* Swap pointer #i and pointer #j */
++ swap_ptr = ptrs[i];
++ ptrs[i] = ptrs[j];
++ ptrs[j] = swap_ptr;
++ }
++ else
++ {
++ /*
++ We need to preserve the score section
++ ordering, so we do it the slow way if i and
++ j are in different scoring sections.
++ */
++ gint k;
++ swap_ptr = ptrs[i];
++ ptrs[i] = ptrs[j];
++
++ for (k = j; k >= (i + 2); k--)
++ {
++ ptrs[k] = ptrs[k - 1];
++ }
++
++ ptrs[i + 1] = swap_ptr;
++ }
++ }
++
++ /* Create a new list from the pointer array */
++ for (i = 0; i < len; i++)
++ {
++ list = g_list_append(list, ptrs[i]);
++ }
+
+- j = random() % len;
+- list = ptrs[j];
+- ptrs[j]->next = NULL;
+- ptrs[j] = ptrs[0];
++ g_free(ptrs);
+
+- for (i = 1; i < len; i++)
+- {
+- j = random() % (len - i);
+- list->prev = ptrs[i + j];
+- ptrs[i + j]->next = list;
+- list = ptrs[i + j];
+- ptrs[i + j] = ptrs[i];
+- }
+- list->prev = NULL;
++ return list;
++}
++
++static GList *old_playlist_shuffle_list(GList *list)
++{
++ /* Caller should hold playlist mutex */
++ /*
++ * Note that this doesn't make a copy of the original list.
++ * The pointer to the original list is not valid after this
++ * fuction is run.
++ */
++ int len = g_list_length(list);
++ gint i;
++ GList *iterator;
++ PlaylistEntry **ptrs;
++
++ if (len == 0)
++ return NULL;
++
++ ptrs = g_new(PlaylistEntry *, len);
++
++ /* Convert the list into an array of pointers */
++ for (iterator = list, i = 0; i < len; iterator = g_list_next(iterator), i++)
++ {
++ ptrs[i] = (PlaylistEntry *)(iterator->data);
++
++ // Shuffling songs voids information about user listening preferences
++ ptrs[i]->moved = FALSE;
++ }
++ g_list_free(list);
++ list = NULL;
++
++ /* Shuffle the pointer array */
++ for (i = 0; i < len; i++)
++ {
++ PlaylistEntry *swap_ptr;
++ gint j;
++
++ /* Pick a random song among the ones that are left */
++ j = (rand() % (len - i)) + i;
++
++ /* Swap pointer #i and pointer #j */
++ swap_ptr = ptrs[i];
++ ptrs[i] = ptrs[j];
++ ptrs[j] = swap_ptr;
++ }
++
++ /* Create a new list from the pointer array */
++ for (i = 0; i < len; i++)
++ {
++ list = g_list_append(list, ptrs[i]);
++ }
+
+ g_free(ptrs);
+
+ return list;
+ }
+
++static GList *playlist_shuffle_list(GList *list)
++{
++ if (cfg.dynamic_taste_detection)
++ {
++ return smart_playlist_shuffle_list(list);
++ }
++ else
++ {
++ return old_playlist_shuffle_list(list);
++ }
++}
++
+ void playlist_random(void)
+ {
++ GList *for_each;
++
+ PL_LOCK();
+
+ playlist = playlist_shuffle_list(playlist);
+
++ /* Remove all moved-marks from the playlist */
++
++ for (for_each = playlist;
++ for_each != NULL;
++ for_each = g_list_next(for_each))
++ {
++ ((PlaylistEntry *) for_each->data)->moved = FALSE;
++ }
++
+ PL_UNLOCK();
+ }
+
+@@ -1710,7 +2052,52 @@
+ PL_UNLOCK();
+ return num;
+ }
+-
++
++gint playlist_get_single_selection(void)
++{
++ // Returns the playlist position of the current selection. If
++ // 0 or > 1 songs are selected, this method returns -1.
++ GList *iterator;
++ gint currentPos;
++
++ gint selectedPos = -1;
++
++ for (iterator = get_playlist(), currentPos = 0;
++ iterator != NULL;
++ iterator = g_list_next(iterator), currentPos++)
++ {
++ if (((PlaylistEntry *)(iterator->data))->selected)
++ {
++ if (selectedPos == -1)
++ {
++ // First selected song found
++ selectedPos = currentPos;
++ }
++ else
++ {
++ // > 1 selected song found
++ return -1;
++ }
++ }
++ }
++
++ return selectedPos;
++}
++
++gboolean playlist_is_selected(gint pos)
++{
++ GList *node;
++ gboolean isSelected = 0;
++
++ PL_LOCK();
++ if ((node = g_list_nth(get_playlist(), pos)) != NULL)
++ {
++ isSelected = ((PlaylistEntry *)(node->data))->selected;
++ }
++ PL_UNLOCK();
++
++ return isSelected;
++}
+
+ static void playlist_generate_shuffle_list(void)
+ {
+diff -ruN dtd-old/xmms/playlist.h dtd-new/xmms/playlist.h
+--- dtd-old/xmms/playlist.h 2003-09-08 13:31:40.000000000 +0200
++++ dtd-new/xmms/playlist.h 2003-09-08 14:14:13.000000000 +0200
+@@ -26,7 +26,8 @@
+ gchar *title;
+ gchar *sort;
+ gint length;
+- gboolean selected;
++ gboolean selected;
++ gboolean moved;
+ }
+ PlaylistEntry;
+
+@@ -81,10 +82,15 @@
+ void playlist_delete_filenames(GList *filenames);
+ gchar* playlist_get_filename(gint pos);
+ gchar* playlist_get_songtitle(gint pos);
++gint playlist_get_score(gint pos);
++void playlist_set_score(gint pos, gint score);
++const gchar* playlist_filename2songtitle(const gchar *filename);
+ gint playlist_get_songtime(gint pos);
+ GList * playlist_get_selected(void);
+ GList * playlist_get_selected_list(void);
+ int playlist_get_num_selected(void);
++gboolean playlist_is_selected(gint pos);
++gint playlist_get_single_selection(void);
+ void playlist_get_total_time(gulong *total_time, gulong *selection_time, gboolean *total_more, gboolean *selection_more);
+ void playlist_select_all(gboolean set);
+ void playlist_select_range(int min, int max, gboolean sel);
+diff -ruN dtd-old/xmms/playlist_list.c dtd-new/xmms/playlist_list.c
+--- dtd-old/xmms/playlist_list.c 2003-09-08 13:31:40.000000000 +0200
++++ dtd-new/xmms/playlist_list.c 2003-09-08 13:46:19.000000000 +0200
+@@ -24,6 +24,8 @@
+ #endif
+ #include <X11/Xatom.h>
+
++static gint playlist_move_delta = 0;
++static PlaylistEntry *playlist_last_moved_song = NULL;
+ static GdkFont *playlist_list_font = NULL;
+
+ static int playlist_list_auto_drag_down_func(gpointer data)
+@@ -61,24 +63,35 @@
+ GList *list;
+
+ PL_LOCK();
++
++ /* If the first song is selected... */
+ if ((list = get_playlist()) == NULL)
+ {
+ PL_UNLOCK();
+ return;
+ }
++
+ if (((PlaylistEntry *) list->data)->selected)
+ {
+- /* We are at the top */
++ /* ... don't move. */
+ PL_UNLOCK();
+ return;
+ }
+- while (list)
++
++ while (list)
+ {
+- if (((PlaylistEntry *) list->data)->selected)
++ if (((PlaylistEntry *) list->data)->selected)
++ {
++ playlist_last_moved_song =
++ (PlaylistEntry *) list->data;
+ glist_moveup(list);
++ }
+ list = g_list_next(list);
+ }
+ PL_UNLOCK();
++
++ playlist_move_delta--;
++
+ if (pl->pl_prev_selected != -1)
+ pl->pl_prev_selected--;
+ if (pl->pl_prev_min != -1)
+@@ -92,24 +105,36 @@
+ GList *list;
+
+ PL_LOCK();
++
++ /* If the last song is selected... */
+ if ((list = g_list_last(get_playlist())) == NULL)
+ {
+ PL_UNLOCK();
+ return;
+ }
++
+ if (((PlaylistEntry *) list->data)->selected)
+ {
+- /* We are at the bottom */
++ /* ... don't move. */
+ PL_UNLOCK();
+ return;
+ }
++
+ while (list)
+ {
+ if (((PlaylistEntry *) list->data)->selected)
++ {
++ playlist_last_moved_song =
++ (PlaylistEntry *) list->data;
+ glist_movedown(list);
++ }
+ list = g_list_previous(list);
+ }
++
+ PL_UNLOCK();
++
++ playlist_move_delta++;
++
+ if (pl->pl_prev_selected != -1)
+ pl->pl_prev_selected++;
+ if (pl->pl_prev_min != -1)
+@@ -177,6 +202,7 @@
+ playlist_play();
+ }
+ pl->pl_dragging = TRUE;
++ playlist_move_delta = 0;
+ playlistwin_update_list();
+ }
+ }
+@@ -244,11 +270,36 @@
+ }
+ }
+
++int playlist_n_selected_songs(void)
++{
++ int n_selected = 0;
++ GList *list;
++
++ for (list = get_playlist();
++ list != NULL;
++ list = g_list_next(list))
++ {
++ if (((PlaylistEntry *) list->data)->selected)
++ n_selected++;
++ }
++
++ return n_selected;
++}
++
+ void playlist_list_button_release_cb(GtkWidget * widget, GdkEventButton * event, PlayList_List * pl)
+ {
+ pl->pl_dragging = FALSE;
+ pl->pl_auto_drag_down = FALSE;
+ pl->pl_auto_drag_up = FALSE;
++
++ if ((playlist_move_delta < 0) && (playlist_n_selected_songs() == 1))
++ {
++ /* Exactly one song has been moved upwards, mark it as moved. */
++ playlist_last_moved_song->moved = TRUE;
++ }
++
++ playlist_move_delta = 0;
++ playlist_last_moved_song = NULL;
+ }
+
+ #ifdef HAVE_WCHAR_H
+diff -ruN dtd-old/xmms/playlistwin.c dtd-new/xmms/playlistwin.c
+--- dtd-old/xmms/playlistwin.c 2003-09-08 13:31:40.000000000 +0200
++++ dtd-new/xmms/playlistwin.c 2003-09-08 14:16:39.000000000 +0200
+@@ -17,6 +17,7 @@
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
++#include <assert.h>
+ #include "xmms.h"
+ #include "libxmms/dirbrowser.h"
+ #include "libxmms/util.h"
+@@ -24,7 +25,7 @@
+ GtkWidget *playlistwin;
+ static GtkWidget *playlistwin_url_window = NULL;
+ static GtkItemFactory *playlistwin_sort_menu, *playlistwin_sub_menu;
+-static GtkItemFactory *playlistwin_popup_menu, *playlistwin_save_menu;
++static GtkItemFactory *playlistwin_popup_menu, *playlistwin_save_menu, *playlistwin_dtd_menu;
+
+ static GdkPixmap *playlistwin_bg;
+ static GdkBitmap *playlistwin_mask;
+@@ -68,13 +69,15 @@
+ SEL_INV, SEL_ZERO, SEL_ALL,
+ MISC_SORT, MISC_FILEINFO, MISC_MISCOPTS,
+ PLIST_NEW, PLIST_SAVE, PLIST_LOAD,
+- SEL_LOOKUP,
++ SEL_LOOKUP, DTD_GOODSONG, DTD_BADSONG,
++ DTD_DISSOCIATE
+ };
+
+ void playlistwin_sort_menu_callback(gpointer cb_data, guint action, GtkWidget * w);
+ void playlistwin_sub_menu_callback(gpointer cb_data, guint action, GtkWidget * w);
+ void playlistwin_save_type_cb(gpointer cb_data, guint action, GtkWidget * w);
+ void playlistwin_set_hints(void);
++void playlistwin_dissociate_callback(GtkMenuItem *menuitem, gpointer ignored);
+
+ enum
+ {
+@@ -151,7 +154,11 @@
+ GtkItemFactoryEntry playlistwin_popup_menu_entries[] =
+ {
+ {N_("/View File Info"), NULL, playlistwin_popup_menu_callback, MISC_FILEINFO, "<Item>"},
++
+ {N_("/-"), NULL, NULL, 0, "<Separator>"},
++
++ {N_("/Dynamic Taste Detection"), NULL, NULL, 0, "<Item>"},
++
+ {N_("/Add"), NULL, NULL, 0, "<Branch>"},
+ {N_("/Add/File"), NULL, playlistwin_popup_menu_callback, ADD_FILE, "<Item>"},
+ {N_("/Add/Directory"), NULL, playlistwin_popup_menu_callback, ADD_DIR, "<Item>"},
+@@ -182,6 +189,21 @@
+ sizeof(playlistwin_popup_menu_entries) /
+ sizeof(playlistwin_popup_menu_entries[0]);
+
++GtkItemFactoryEntry playlistwin_dtd_menu_entries[] =
++{
++ {N_("/I Like this Song"),
++ NULL, playlistwin_popup_menu_callback, DTD_GOODSONG,
++ "<RadioItem>"},
++ {N_("/I Don't Like this Song"),
++ NULL, playlistwin_popup_menu_callback, DTD_BADSONG,
++ /* <RadioItem> */ "/I Like this Song"},
++ {N_("/Dissociate From"), NULL, NULL, 0, "<Item>"}
++};
++
++static const int playlistwin_dtd_menu_entries_num =
++ sizeof(playlistwin_dtd_menu_entries) /
++ sizeof(playlistwin_dtd_menu_entries[0]);
++
+ void playlistwin_draw_frame(void);
+
+ static void playlistwin_update_info(void)
+@@ -1030,6 +1052,35 @@
+ case PLIST_LOAD:
+ playlistwin_show_load_filesel();
+ break;
++
++ /* DTD button */
++ case DTD_GOODSONG: {
++ gint score;
++ gint selected_playlist_pos = playlist_get_single_selection();
++
++ assert(selected_playlist_pos > -1);
++ score = playlist_get_score(selected_playlist_pos);
++ if (score < 0)
++ {
++ playlist_set_score(selected_playlist_pos, 0);
++ }
++
++ break;
++ }
++
++ case DTD_BADSONG: {
++ gint score;
++ gint selected_playlist_pos = playlist_get_single_selection();
++
++ assert(selected_playlist_pos > -1);
++ score = playlist_get_score(selected_playlist_pos);
++ if (score >= 0)
++ {
++ playlist_set_score(selected_playlist_pos, -1);
++ }
++
++ break;
++ }
+ }
+ }
+
+@@ -1053,6 +1104,133 @@
+ inside_widget(x, y, playlistwin_sscroll_down));
+ }
+
++gchar *playlistwin_fileName2songName(const gchar *fileName)
++{
++ const gchar *afterLastSlash, *beforeLastDot, *c;
++ gchar *returnMe;
++ gint i;
++ const gchar *loadedTitle =
++ playlist_filename2songtitle(fileName);
++
++ if (loadedTitle != NULL)
++ {
++ return g_strdup(loadedTitle);
++ }
++
++ // Ditch the last slash and everything before it, as well as
++ // the last dot and everything after it before returning it.
++ afterLastSlash = strrchr(fileName, '/');
++ if (afterLastSlash != NULL)
++ {
++ afterLastSlash++;
++ }
++ else
++ {
++ afterLastSlash = fileName;
++ }
++
++ beforeLastDot = strrchr(fileName, '.');
++ if ((beforeLastDot != NULL) && (beforeLastDot > afterLastSlash))
++ {
++ beforeLastDot--;
++ }
++ else
++ {
++ beforeLastDot = fileName + strlen(fileName) - 1;
++ }
++
++ if ((afterLastSlash >= beforeLastDot) ||
++ (beforeLastDot <= fileName) ||
++ ((afterLastSlash - fileName) >= strlen(fileName)))
++ {
++ return g_strdup(fileName);
++ }
++
++ // Copy from after last slash to before last dot
++ returnMe = g_new(gchar, (beforeLastDot - afterLastSlash) + 1);
++ i = 0;
++ for (c = afterLastSlash; c <= beforeLastDot; c++)
++ {
++ returnMe[i++] = *c;
++ }
++ returnMe[i] = '\0';
++
++ return returnMe;
++}
++
++void playlistwin_remove_dissociation_menu(GtkMenuItem *attachPoint)
++{
++ // Nuke any previous dissociations menu
++
++ // FIXME: I have absolutely no clue to how GTK+ refcounting
++ // works. This implementation may or may not leak memory.
++ // Candidates for memory leaks are the user_data fields of the
++ // labels (they should be freed).
++ // /Johan Walles - jan 05 / 2002
++ gtk_menu_item_remove_submenu(attachPoint);
++}
++
++void playlistwin_setup_dissociation_menu(GtkMenuItem *attachPoint,
++ gint selected_playlist_pos)
++{
++ const GList *recommendations = NULL;
++ const gchar *filename = playlist_get_filename(selected_playlist_pos);
++
++ // Ditch the previous dissociations menu (if any)
++ playlistwin_remove_dissociation_menu(attachPoint);
++
++ recommendations =
++ dtd_get_recommendations(filename);
++
++ if (recommendations != NULL)
++ {
++ // Create a new menu
++ GtkWidget *dissociateMenu = gtk_menu_new();
++ const GList *iterator;
++
++ // Associate it with the selected song
++ gtk_object_set_user_data(GTK_OBJECT(dissociateMenu), g_strdup(filename));
++
++ // For all recommendations...
++ for (iterator = recommendations;
++ iterator != NULL;
++ iterator = g_list_next(iterator))
++ {
++ const gchar *filename = ((DtdNextSong *)(iterator->data))->filename;
++ gchar *songName = playlistwin_fileName2songName(filename);
++ GtkWidget *newMenuItem;
++
++ // ... create a new menu entry...
++ newMenuItem = gtk_menu_item_new_with_label(songName);
++ g_free(songName);
++
++ // ... with its user_data set to point
++ // to the associated file name...
++ gtk_object_set_user_data(GTK_OBJECT(newMenuItem), g_strdup(filename));
++
++ // ... tell it what to do on receiving a click...
++ gtk_signal_connect(GTK_OBJECT(newMenuItem),
++ "activate",
++ GTK_SIGNAL_FUNC(playlistwin_dissociate_callback),
++ NULL);
++
++ // ... and add it to the new menu.
++ gtk_menu_append(GTK_MENU(dissociateMenu), newMenuItem);
++ }
++
++ // Enable our new dissociations menu
++ gtk_widget_show_all(dissociateMenu);
++ gtk_menu_item_set_submenu(attachPoint, dissociateMenu);
++ gtk_widget_set_sensitive(GTK_WIDGET(attachPoint), 1);
++ }
++ else
++ {
++ // ... or disable the dissociation menu heading
++ // if there is nothing to dissociate from.
++ gtk_widget_set_sensitive(GTK_WIDGET(attachPoint), 0);
++ }
++}
++
+ #define REGION_L(x1,x2,y1,y2) \
+ (event->x >= (x1) && event->x < (x2) && \
+ event->y >= cfg.playlist_height - (y1) && \
+@@ -1227,18 +1405,101 @@
+ else if (event->button == 3 &&
+ inside_widget(event->x, event->y, playlistwin_list))
+ {
+- int pos, sensitive;
++ // FIXME: This block is much too long, put it in its
++ // own function
++
++ gint clicked_playlist_pos, selected_playlist_pos;
++ gboolean sensitive;
++
+ GtkWidget *w;
+- pos = playlist_list_get_playlist_position(playlistwin_list,
+- event->x, event->y);
+- sensitive = pos != -1;
++
++ clicked_playlist_pos =
++ playlist_list_get_playlist_position(playlistwin_list,
++ event->x, event->y);
++
++ // Unless the clicked song is part of a (multiple)
++ // selection...
++ if (!playlist_is_selected(clicked_playlist_pos))
++ {
++ // ... select just the current song before
++ // displaying the menu
++ playlist_select_all(0);
++ playlist_select_range(clicked_playlist_pos, clicked_playlist_pos, 1);
++ playlistwin_update_list();
++ }
++
++ selected_playlist_pos = playlist_get_single_selection();
++ sensitive = (selected_playlist_pos != -1);
++
++ // Disable "View File Info" if not exactly one song is selected
+ w = gtk_item_factory_get_widget(playlistwin_popup_menu,
+ "/View File Info");
+ gtk_widget_set_sensitive(w, sensitive);
++
++ // Disable the DTD menu hierarchy if not exactly one
++ // song is selected
++ // FIXME: or if it's disabled in the prefs
++ w = gtk_item_factory_get_widget(playlistwin_popup_menu,
++ "/Dynamic Taste Detection");
++ gtk_widget_set_sensitive(w, sensitive);
++
++ if (sensitive)
++ {
++ GtkWidget *playlistwin_dtd_menu_widget =
++ gtk_item_factory_get_widget(playlistwin_dtd_menu, "<Main>");
++
++ gtk_widget_show_all(playlistwin_dtd_menu_widget);
++ gtk_menu_item_set_submenu(GTK_MENU_ITEM(w), playlistwin_dtd_menu_widget);
++ }
++ else
++ {
++ gtk_menu_item_remove_submenu(GTK_MENU_ITEM(w));
++ gtk_widget_hide_all(gtk_item_factory_get_widget(playlistwin_dtd_menu, "<Main>"));
++ }
++
++ if (selected_playlist_pos != -1)
++ {
++ // Enable either "/I Like this Song" (if the
++ // song scores >= 0) or "Bad Song" (if the
++ // song scores < 0).
++ gint score = dtd_get_score(playlist_get_filename(selected_playlist_pos));
++
++ w = gtk_item_factory_get_widget(playlistwin_dtd_menu,
++ "/I Like this Song");
++ if (score >= 0)
++ {
++ gtk_check_menu_item_set_state(GTK_CHECK_MENU_ITEM(w), 1);
++ }
++ gtk_widget_set_sensitive(w, 1);
++ w = gtk_item_factory_get_widget(playlistwin_dtd_menu,
++ "/I Don't Like this Song");
++ if (score < 0)
++ {
++ gtk_check_menu_item_set_state(GTK_CHECK_MENU_ITEM(w), 1);
++ }
++ gtk_widget_set_sensitive(w, 1);
++ }
++ else
++ {
++ // We don't do ratings of multiple songs at once
++ w = gtk_item_factory_get_widget(playlistwin_dtd_menu,
++ "/I Like this Song");
++ gtk_widget_set_sensitive(w, 0);
++ gtk_check_menu_item_set_state(GTK_CHECK_MENU_ITEM(w), 0);
++ w = gtk_item_factory_get_widget(playlistwin_dtd_menu,
++ "/I Don't Like this Song");
++ gtk_widget_set_sensitive(w, 0);
++ gtk_check_menu_item_set_state(GTK_CHECK_MENU_ITEM(w), 0);
++ }
+
++ // Create the dissociation menu
++ w = gtk_item_factory_get_widget(playlistwin_dtd_menu,
++ "/Dissociate From");
++ playlistwin_setup_dissociation_menu(GTK_MENU_ITEM(w), selected_playlist_pos);
++
+ playlistwin_set_sensitive_sortmenu();
+ util_item_factory_popup_with_data(playlistwin_popup_menu,
+- GINT_TO_POINTER(pos), NULL,
++ GINT_TO_POINTER(selected_playlist_pos), NULL,
+ event->x_root,
+ event->y_root + 5,
+ 3, event->time);
+@@ -1946,6 +2207,11 @@
+ cfg.playlist_height,
+ gdk_rgb_get_visual()->depth);
+
++ playlistwin_dtd_menu = gtk_item_factory_new(GTK_TYPE_MENU, "<Main>", NULL);
++ gtk_item_factory_create_items(GTK_ITEM_FACTORY(playlistwin_dtd_menu),
++ playlistwin_dtd_menu_entries_num,
++ playlistwin_dtd_menu_entries, NULL);
++
+ playlistwin_popup_menu =
+ gtk_item_factory_new(GTK_TYPE_MENU, "<Main>", NULL);
+ gtk_item_factory_set_translate_func(playlistwin_popup_menu,
+@@ -2022,6 +2288,23 @@
+ tbutton_set_toggled(mainwin_pl, FALSE);
+ }
+
++void playlistwin_dissociate_callback(GtkMenuItem *menuitem,
++ gpointer ignored)
++{
++ gchar *first, *second;
++
++ // Find out "first" by checking the user data of the menuitem
++ // that was activated
++ first = (gchar *)gtk_object_get_user_data(GTK_OBJECT(menuitem));
++
++ // Find out "second" by checking the user data of the menu
++ // that is the parent of the menuitem that was activated
++ second = (gchar *)gtk_object_get_user_data(GTK_OBJECT(GTK_WIDGET(menuitem)->parent));
++
++ dtd_dissociate(first, second);
++}
++
++
+ void playlistwin_popup_menu_callback(gpointer cb_data, guint action, GtkWidget * w)
+ {
+ int pos = GPOINTER_TO_INT(gtk_item_factory_popup_data_from_widget(w));
+@@ -2034,6 +2317,17 @@
+ case SEL_LOOKUP:
+ playlist_read_info_selection();
+ break;
++ case DTD_DISSOCIATE:
++
++ break;
++ case DTD_GOODSONG:
++ case DTD_BADSONG:
++ // Act only upon the active item
++ if (GTK_CHECK_MENU_ITEM(w)->active)
++ {
++ playlistwin_popup_handler(action);
++ }
++ break;
+ default:
+ playlistwin_popup_handler(action);
+ }
+diff -ruN dtd-old/xmms/prefswin.c dtd-new/xmms/prefswin.c
+--- dtd-old/xmms/prefswin.c 2003-09-08 13:31:40.000000000 +0200
++++ dtd-new/xmms/prefswin.c 2003-09-08 13:46:19.000000000 +0200
+@@ -1042,6 +1042,8 @@
+
+ gtk_notebook_append_page(GTK_NOTEBOOK(prefswin_notebook), prefswin_options_vbox, gtk_label_new(_("Options")));
+
++ prefswin_option_new_with_label_to_table(&cfg.dynamic_taste_detection, _("Dynamic taste detection"), GTK_TABLE(options_table), 1, 10);
++
+ /*
+ * Fonts page
+ */
+diff -ruN dtd-old/xmms/xmms.h dtd-new/xmms/xmms.h
+--- dtd-old/xmms/xmms.h 2001-03-14 15:06:39.000000000 +0100
++++ dtd-new/xmms/xmms.h 2003-09-08 13:46:19.000000000 +0200
+@@ -81,6 +81,7 @@
+ #include "sm.h"
+ #include "dnd.h"
+ #include "urldecode.h"
++#include "dtd.h"
+
+ #include "config.h"
+
diff --git a/media-sound/xmms/files/xmms-1.2.8-jump.patch b/media-sound/xmms/files/xmms-1.2.8-jump.patch
new file mode 100644
index 000000000000..5ebd59b3d08a
--- /dev/null
+++ b/media-sound/xmms/files/xmms-1.2.8-jump.patch
@@ -0,0 +1,76 @@
+diff -Naur xmms-1.2.8/libxmms/xmmsctrl.c xmms-1.2.8-new/libxmms/xmmsctrl.c
+--- xmms-1.2.8/libxmms/xmmsctrl.c 2003-09-04 15:48:34.000000000 +0200
++++ xmms-1.2.8-new/libxmms/xmmsctrl.c 2003-09-06 18:18:53.000000000 +0200
+@@ -381,6 +381,11 @@
+ remote_cmd(session, CMD_PLAY_PAUSE);
+ }
+
++void xmms_remote_show_jump_box(gint session)
++{
++ remote_cmd(session, CMD_SHOW_JUMP_BOX);
++}
++
+ gboolean xmms_remote_is_playing(gint session)
+ {
+ return remote_get_gboolean(session, CMD_IS_PLAYING);
+diff -Naur xmms-1.2.8/libxmms/xmmsctrl.h xmms-1.2.8-new/libxmms/xmmsctrl.h
+--- xmms-1.2.8/libxmms/xmmsctrl.h 2003-06-09 15:22:10.000000000 +0200
++++ xmms-1.2.8-new/libxmms/xmmsctrl.h 2003-09-06 18:18:53.000000000 +0200
+@@ -61,6 +61,7 @@
+ gboolean xmms_remote_is_pl_win(gint session);
+ gboolean xmms_remote_is_eq_win(gint session);
+ void xmms_remote_show_prefs_box(gint session);
++void xmms_remote_show_jump_box(gint session);
+ void xmms_remote_toggle_aot(gint session, gboolean ontop);
+ void xmms_remote_eject(gint session);
+ void xmms_remote_playlist_prev(gint session);
+diff -Naur xmms-1.2.8/xmms/controlsocket.c xmms-1.2.8-new/xmms/controlsocket.c
+--- xmms-1.2.8/xmms/controlsocket.c 2003-09-04 15:48:34.000000000 +0200
++++ xmms-1.2.8-new/xmms/controlsocket.c 2003-09-06 18:18:53.000000000 +0200
+@@ -506,6 +506,9 @@
+ case CMD_SET_SKIN:
+ load_skin(data);
+ break;
++ case CMD_SHOW_JUMP_BOX:
++ show_jump_to_file();
++ break;
+ case CMD_PL_WIN_TOGGLE:
+ tbool = *((gboolean *) data);
+ playlistwin_show(!!tbool);
+diff -Naur xmms-1.2.8/xmms/controlsocket.h xmms-1.2.8-new/xmms/controlsocket.h
+--- xmms-1.2.8/xmms/controlsocket.h 2003-06-09 15:22:10.000000000 +0200
++++ xmms-1.2.8-new/xmms/controlsocket.h 2003-09-06 18:18:53.000000000 +0200
+@@ -46,6 +46,7 @@
+ CMD_GET_EQ, CMD_GET_EQ_PREAMP, CMD_GET_EQ_BAND,
+ CMD_SET_EQ, CMD_SET_EQ_PREAMP, CMD_SET_EQ_BAND,
+ CMD_QUIT, CMD_PLAYLIST_INS_URL_STRING, CMD_PLAYLIST_INS, CMD_PLAY_PAUSE,
++ CMD_SHOW_JUMP_BOX
+ };
+
+ typedef struct
+diff -Naur xmms-1.2.8/xmms/main.c xmms-1.2.8-new/xmms/main.c
+--- xmms-1.2.8/xmms/main.c 2003-09-02 15:01:40.000000000 +0200
++++ xmms-1.2.8-new/xmms/main.c 2003-09-06 18:18:53.000000000 +0200
+@@ -1847,6 +1847,11 @@
+ GTK_CLIST(clist)->focus_row = GPOINTER_TO_INT(GTK_CLIST(clist)->selection->data);
+ }
+
++void show_jump_to_file(void)
++{
++ mainwin_jump_to_file();
++}
++
+ static gboolean mainwin_configure(GtkWidget * window, GdkEventConfigure *event, gpointer data)
+ {
+ if (!GTK_WIDGET_VISIBLE(window))
+diff -Naur xmms-1.2.8/xmms/main.h xmms-1.2.8-new/xmms/main.h
+--- xmms-1.2.8/xmms/main.h 2002-10-06 18:35:27.000000000 +0200
++++ xmms-1.2.8-new/xmms/main.h 2003-09-06 18:18:53.000000000 +0200
+@@ -117,6 +117,7 @@
+ void mainwin_set_shade_menu_cb(gboolean shaded);
+ void mainwin_set_shade(gboolean shaded);
+ void mainwin_shade_toggle(void);
++void show_jump_to_file(void);
+
+ #define PLAYER_HEIGHT ((cfg.player_shaded ? 14 : 116) * (cfg.doublesize + 1))
+ #define PLAYER_WIDTH (275 * (cfg.doublesize + 1))
diff --git a/media-sound/xmms/files/xmms-1.2.8-russian-charset.patch b/media-sound/xmms/files/xmms-1.2.8-russian-charset.patch
new file mode 100644
index 000000000000..57ffb8612f89
--- /dev/null
+++ b/media-sound/xmms/files/xmms-1.2.8-russian-charset.patch
@@ -0,0 +1,2174 @@
+diff -Naur xmms-1.2.8/Input/mpg123/fileinfo.c xmms-1.2.8-new/Input/mpg123/fileinfo.c
+--- xmms-1.2.8/Input/mpg123/fileinfo.c 2003-07-14 15:24:28.000000000 +0200
++++ xmms-1.2.8-new/Input/mpg123/fileinfo.c 2003-09-06 19:42:29.000000000 +0200
+@@ -26,6 +26,10 @@
+ #include <libxmms/xentry.h>
+ #include "mpg123.h"
+
++#include "libxmms/charset.h"
++#include <gdk/gdkkeysyms.h>
++#include "xmms/xmms.h"
++
+ static GtkWidget *window = NULL;
+ static GtkWidget *filename_entry, *id3_frame;
+ static GtkWidget *title_entry, *artist_entry, *album_entry, *year_entry, *tracknum_entry, *comment_entry;
+@@ -46,20 +50,35 @@
+ {
+ gint stripped_len;
+ gchar *text;
++ gchar *ctag;
++ gint clength;
+
++ ctag=xmms_charset_recode_id3(tag,length,&clength);
++ if (ctag) {
++ tag=ctag;
++ length=clength;
++ }
++
+ stripped_len = mpg123_strip_spaces(tag, length);
+ text = g_strdup_printf("%-*.*s", stripped_len, stripped_len, tag);
+ gtk_entry_set_text(entry, text);
+ g_free(text);
++ if (ctag) g_free(ctag);
+ }
+
+ static void get_entry_tag(GtkEntry * entry, gchar * tag, gint length)
+ {
+ gchar *text;
++ gchar *ctext;
+
+ text = gtk_entry_get_text(entry);
++ ctext = xmms_charset_recode_output(text,length,NULL);
++ if (ctext) text=ctext;
++
+ memset(tag, ' ', length);
+ memcpy(tag, text, strlen(text) > length ? length : strlen(text));
++
++ if (ctext) g_free(ctext);
+ }
+
+ static gint find_genre_id(gchar * text)
+@@ -225,6 +244,31 @@
+ }
+ }
+
++static int restore_focus;
++
++gboolean mpg123_keypress_cb(GtkWidget * w, GdkEventKey * event, gpointer save) {
++ switch(event->keyval) {
++ case GDK_Return:
++ restore_focus=1;
++ gtk_signal_emit_by_name(GTK_OBJECT(save), "clicked", NULL);
++ break;
++ case GDK_Escape:
++ restore_focus=1;
++ break;
++ }
++ return TRUE;
++}
++
++void gtk_widget_destroyed_focus(GtkWidget *widget, GtkWidget **widget_pointer) {
++ gtk_widget_destroyed(widget,widget_pointer);
++ if (restore_focus) {
++ gtk_widget_hide(playlistwin);
++ gtk_widget_show(playlistwin);
++// playlistwin_real_hide();
++// playlistwin_real_show();
++ }
++}
++
+ void mpg123_file_info_box(char *filename)
+ {
+ gint i;
+@@ -233,7 +277,10 @@
+ gchar *tmp, *title;
+ const gchar *emphasis[4];
+ const gchar *bool_label[2];
++ gchar *cfilename;
+
++ restore_focus=0;
++
+ emphasis[0] = _("None");
+ emphasis[1] = _("50/15 ms");
+ emphasis[2] = "";
+@@ -247,10 +294,11 @@
+ GtkWidget *mpeg_frame, *mpeg_box;
+ GtkWidget *label, *filename_hbox;
+ GtkWidget *bbox, *save, *remove_id3, *cancel;
++ GtkAccelGroup *ag;
+
+ window = gtk_window_new(GTK_WINDOW_DIALOG);
+ gtk_window_set_policy(GTK_WINDOW(window), FALSE, FALSE, FALSE);
+- gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &window);
++ gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed_focus), &window);
+ gtk_container_set_border_width(GTK_CONTAINER(window), 10);
+
+ vbox = gtk_vbox_new(FALSE, 10);
+@@ -392,6 +440,18 @@
+ gtk_label_set_justify(GTK_LABEL(mpeg_fileinfo), GTK_JUSTIFY_LEFT);
+ gtk_box_pack_start(GTK_BOX(mpeg_box), mpeg_fileinfo, FALSE, FALSE, 0);
+
++ ag = gtk_accel_group_new();
++ gtk_accel_group_add(ag, GDK_Escape, 0, 0, GTK_OBJECT(cancel), "clicked");
++ gtk_accel_group_add(ag, GDK_Return, 0, 0, GTK_OBJECT(save), "clicked");
++ gtk_window_add_accel_group(GTK_WINDOW(window), ag);
++/* gtk_signal_connect(GTK_OBJECT(title_entry), "key_press_event", GTK_SIGNAL_FUNC(mpg123_keypress_cb), save);
++ gtk_signal_connect(GTK_OBJECT(artist_entry), "key_press_event", GTK_SIGNAL_FUNC(mpg123_keypress_cb), save);
++ gtk_signal_connect(GTK_OBJECT(album_entry), "key_press_event", GTK_SIGNAL_FUNC(mpg123_keypress_cb), save);
++ gtk_signal_connect(GTK_OBJECT(comment_entry), "key_press_event", GTK_SIGNAL_FUNC(mpg123_keypress_cb), save);
++ gtk_signal_connect(GTK_OBJECT(year_entry), "key_press_event", GTK_SIGNAL_FUNC(mpg123_keypress_cb), save);
++ gtk_signal_connect(GTK_OBJECT(tracknum_entry), "key_press_event", GTK_SIGNAL_FUNC(mpg123_keypress_cb), save);*/
++ gtk_signal_connect(GTK_OBJECT(window),"key_press_event",GTK_SIGNAL_FUNC(mpg123_keypress_cb),save);
++
+ gtk_widget_show_all(window);
+ }
+
+@@ -399,6 +459,9 @@
+ g_free(current_filename);
+ current_filename = g_strdup(filename);
+
++ cfilename=xmms_charset_recode_fsout(filename,0,NULL);
++ if (cfilename) filename=cfilename;
++
+ title = g_strdup_printf(_("File Info - %s"), g_basename(filename));
+ gtk_window_set_title(GTK_WINDOW(window), title);
+ g_free(title);
+@@ -412,6 +475,8 @@
+ gtk_entry_set_text(GTK_ENTRY(title_entry), title);
+ g_free(title);
+
++ if (cfilename) g_free(cfilename);
++
+ gtk_entry_set_text(GTK_ENTRY(artist_entry), "");
+ gtk_entry_set_text(GTK_ENTRY(album_entry), "");
+ gtk_entry_set_text(GTK_ENTRY(year_entry), "");
+diff -Naur xmms-1.2.8/Input/mpg123/mpg123.c xmms-1.2.8-new/Input/mpg123/mpg123.c
+--- xmms-1.2.8/Input/mpg123/mpg123.c 2003-07-10 15:12:19.000000000 +0200
++++ xmms-1.2.8-new/Input/mpg123/mpg123.c 2003-09-06 19:42:29.000000000 +0200
+@@ -551,6 +551,7 @@
+ {
+ gchar *ret = NULL, *path, *temp;
+ TitleInput *input;
++ gint len;
+
+ XMMS_NEW_TITLEINPUT(input);
+
+@@ -582,9 +583,18 @@
+ /*
+ * Format according to filename.
+ */
++ ret = xmms_charset_recode_fs(g_basename(filename),0,&len);
++ if (ret) {
++ for (;len>=0;len--)
++ if (ret[len]=='.') {
++ ret[len]=0;
++ break;
++ }
++ } else {
+ ret = g_strdup(g_basename(filename));
+ if (extname(ret) != NULL)
+ *(extname(ret) - 1) = '\0'; /* removes period */
++ }
+ }
+
+ return ret;
+diff -Naur xmms-1.2.8/Input/vorbis/vorbis.c xmms-1.2.8-new/Input/vorbis/vorbis.c
+--- xmms-1.2.8/Input/vorbis/vorbis.c 2003-07-14 15:24:28.000000000 +0200
++++ xmms-1.2.8-new/Input/vorbis/vorbis.c 2003-09-06 19:42:29.000000000 +0200
+@@ -47,6 +47,7 @@
+ #include "libxmms/util.h"
+ #include "libxmms/configfile.h"
+ #include "libxmms/titlestring.h"
++#include "libxmms/charset.h"
+ #include <xmms/i18n.h>
+
+ #include "vorbis.h"
+@@ -668,6 +669,7 @@
+ gchar *displaytitle = NULL, *tmp, *path, *temp;
+ vorbis_comment *comment;
+ TitleInput *input;
++ gint len;
+
+ XMMS_NEW_TITLEINPUT(input);
+ path = g_strdup(fn);
+@@ -705,10 +707,20 @@
+
+ if (!displaytitle)
+ {
+- if (!vorbis_is_streaming)
+- displaytitle = g_strdup_printf("%s", g_basename(fn));
+- else
++ if (!vorbis_is_streaming) {
++ displaytitle = xmms_charset_recode_fs(g_basename(fn),0,&len);
++ if (displaytitle) {
++ for (;len>=0;len--)
++ if (displaytitle[len]=='.') {
++ displaytitle[len]=0;
++ break;
++ }
++ } else {
++ displaytitle = g_strdup_printf("%s", g_basename(fn));
++ }
++ } else {
+ displaytitle = vorbis_http_get_title(fn);
++ }
+ }
+
+ return displaytitle;
+diff -Naur xmms-1.2.8/libxmms/charset_auto_russian.h xmms-1.2.8-new/libxmms/charset_auto_russian.h
+--- xmms-1.2.8/libxmms/charset_auto_russian.h 1970-01-01 01:00:00.000000000 +0100
++++ xmms-1.2.8-new/libxmms/charset_auto_russian.h 2003-09-06 19:42:29.000000000 +0200
+@@ -0,0 +1,293 @@
++/* Stealed from GPPL's (http://gppl.terminal.ru) autorecode-xmms1.2.7 patch,
++ changed to support limited size strings */
++
++typedef struct lng_stat {
++ unsigned char a;
++ unsigned char b;
++ int rate;
++} lng_stat;
++
++//statistics calculated from "Moskva-Petushki"
++//1292 elems
++static const lng_stat win[]={
++{'À','À',1}, {'À','Â',1}, {'À','Ç',1}, {'À','È',1}, {'À','Ê',1}, {'À','Ë',1}, {'À','Ì',8}, {'À','Í',2}, {'À','Ï',2}, {'À','Ð',1}, {'À','Ñ',4}, {'À','Ò',2}, {'À','Ô',1}, {'À','Õ',1}, {'À','×',1}, {'À','Ø',1},
++{'À','Ý',1}, {'À','à',1}, {'À','á',6}, {'À','â',60}, {'À','ã',5}, {'À','ä',7}, {'À','å',23}, {'À','æ',2}, {'À','ç',6}, {'À','è',4}, {'À','é',1}, {'À','ê',27}, {'À','ë',15}, {'À','ì',10}, {'À','í',25}, {'À','î',21},
++{'À','ï',51}, {'À','ð',12}, {'À','ñ',13}, {'À','ò',31}, {'À','ó',15}, {'À','ô',1}, {'À','õ',6}, {'À','÷',17}, {'À','ý',8}, {'À','ÿ',20}, {'Á','Â',1}, {'Á','Ä',1}, {'Á','Í',2}, {'Á','Ò',1}, {'Á','à',3}, {'Á','â',2},
++{'Á','å',16}, {'Á','è',1}, {'Á','ë',4}, {'Á','í',3}, {'Á','î',25}, {'Á','ð',10}, {'Á','ó',9}, {'Á','û',8}, {'Â','À',1}, {'Â','Ê',2}, {'Â','Ì',1}, {'Â','Î',3}, {'Â','Ï',8}, {'Â','Ñ',2}, {'Â','Õ',1}, {'Â','Ø',2},
++{'Â','à',15}, {'Â','â',2}, {'Â','ã',3}, {'Â','ä',5}, {'Â','å',98}, {'Â','ç',5}, {'Â','è',7}, {'Â','ê',5}, {'Â','ë',8}, {'Â','ì',7}, {'Â','í',6}, {'Â','î',107}, {'Â','ï',18}, {'Â','ð',2}, {'Â','ñ',74}, {'Â','ò',6},
++{'Â','ó',1}, {'Â','÷',3}, {'Â','û',39}, {'Â','ý',8}, {'Ã','Î',2}, {'Ã','à',6}, {'Ã','ä',16}, {'Ã','å',26}, {'Ã','è',4}, {'Ã','ë',3}, {'Ã','ì',1}, {'Ã','î',31}, {'Ã','ð',7}, {'Ã','ó',2}, {'Ä','à',167}, {'Ä','â',6},
++{'Ä','å',17}, {'Ä','è',3}, {'Ä','ë',7}, {'Ä','ì',3}, {'Ä','í',1}, {'Ä','î',20}, {'Ä','ð',9}, {'Ä','ó',19}, {'Å','Ã',2}, {'Å','Õ',1}, {'Å','Ø',1}, {'Å','â',9}, {'Å','ä',1}, {'Å','å',1}, {'Å','é',1}, {'Å','ë',5},
++{'Å','ì',2}, {'Å','ð',19}, {'Å','ñ',69}, {'Å','ô',1}, {'Å','õ',1}, {'Å','ù',5}, {'Æ','à',5}, {'Æ','ä',1}, {'Æ','å',14}, {'Æ','è',5}, {'Æ','î',2}, {'Æ','ð',1}, {'Ç','È',1}, {'Ç','à',31}, {'Ç','â',3}, {'Ç','å',1},
++{'Ç','è',2}, {'Ç','í',23}, {'Ç','ð',2}, {'Ç','ó',12}, {'È','À',1}, {'È','Â',1}, {'È','Ë',2}, {'È','Ì',2}, {'È','Ñ',2}, {'È','à',2}, {'È','á',8}, {'È','â',78}, {'È','ã',7}, {'È','ä',20}, {'È','å',7}, {'È','æ',1},
++{'È','ç',17}, {'È','è',3}, {'È','ê',27}, {'È','ë',15}, {'È','ì',11}, {'È','í',21}, {'È','î',26}, {'È','ï',21}, {'È','ð',4}, {'È','ñ',21}, {'È','ò',37}, {'È','ó',5}, {'È','ô',2}, {'È','õ',3}, {'È','÷',8}, {'È','ø',1},
++{'È','ý',2}, {'È','ÿ',12}, {'Ê','Ï',3}, {'Ê','à',100}, {'Ê','è',4}, {'Ê','ê',1}, {'Ê','ë',14}, {'Ê','í',4}, {'Ê','î',62}, {'Ê','ï',2}, {'Ê','ð',24}, {'Ê','ò',10}, {'Ê','ó',62}, {'Ë','À',1}, {'Ë','È',1}, {'Ë','Î',2},
++{'Ë','à',7}, {'Ë','å',11}, {'Ë','è',4}, {'Ë','î',12}, {'Ë','ó',15}, {'Ë','þ',3}, {'Ë','ÿ',1}, {'Ì','À',1}, {'Ì','È',1}, {'Ì','Î',1}, {'Ì','Ï',1}, {'Ì','à',31}, {'Ì','â',3}, {'Ì','ã',1}, {'Ì','å',10}, {'Ì','è',44},
++{'Ì','ë',1}, {'Ì','í',19}, {'Ì','î',93}, {'Ì','ð',1}, {'Ì','ó',6}, {'Ì','û',29}, {'Í','À',2}, {'Í','Ç',1}, {'Í','È',3}, {'Í','à',56}, {'Í','å',111}, {'Í','è',47}, {'Í','í',2}, {'Í','î',98}, {'Í','ó',100}, {'Î','Â',1},
++{'Î','Å',2}, {'Î','Í',6}, {'Î','Ð',1}, {'Î','Ò',1}, {'Î','Ó',1}, {'Î','ß',1}, {'Î','á',18}, {'Î','â',1}, {'Î','ã',4}, {'Î','ä',20}, {'Î','ç',2}, {'Î','è',2}, {'Î','ê',7}, {'Î','ë',9}, {'Î','ì',2}, {'Î','í',96},
++{'Î','î',5}, {'Î','ï',17}, {'Î','ð',16}, {'Î','ñ',21}, {'Î','ò',35}, {'Î','÷',13}, {'Î','ù',1}, {'Î','ý',6}, {'Ï','Ñ',3}, {'Ï','à',18}, {'Ï','å',148}, {'Ï','è',11}, {'Ï','ë',9}, {'Ï','î',186}, {'Ï','ð',43}, {'Ï','ó',38},
++{'Ï','ü',4}, {'Ð','Ë',1}, {'Ð','à',22}, {'Ð','å',13}, {'Ð','è',5}, {'Ð','î',13}, {'Ð','ð',1}, {'Ð','ó',1}, {'Ñ','À',2}, {'Ñ','Â',1}, {'Ñ','Ñ',3}, {'Ñ','à',19}, {'Ñ','á',1}, {'Ñ','â',4}, {'Ñ','ä',1}, {'Ñ','å',56},
++{'Ñ','è',16}, {'Ñ','ê',22}, {'Ñ','ë',11}, {'Ñ','ì',12}, {'Ñ','í',6}, {'Ñ','î',26}, {'Ñ','ï',6}, {'Ñ','ð',6}, {'Ñ','ñ',3}, {'Ñ','ò',35}, {'Ñ','ó',4}, {'Ñ','õ',2}, {'Ñ','÷',2}, {'Ñ','û',1}, {'Ñ','ý',1}, {'Ñ','ÿ',1},
++{'Ò','À',4}, {'Ò','Å',1}, {'Ò','È',1}, {'Ò','Î',1}, {'Ò','à',79}, {'Ò','â',1}, {'Ò','å',18}, {'Ò','è',17}, {'Ò','î',34}, {'Ò','ð',11}, {'Ò','ó',31}, {'Ò','û',55}, {'Ò','ü',2}, {'Ò','ÿ',3}, {'Ó','á',2}, {'Ó','â',3},
++{'Ó','ã',1}, {'Ó','ä',3}, {'Ó','å',2}, {'Ó','æ',4}, {'Ó','ç',4}, {'Ó','è',1}, {'Ó','é',2}, {'Ó','ê',1}, {'Ó','ë',2}, {'Ó','ì',9}, {'Ó','í',5}, {'Ó','î',1}, {'Ó','ï',1}, {'Ó','ñ',9}, {'Ó','ò',8}, {'Ó','õ',2},
++{'Ó','÷',1}, {'Ó','ø',1}, {'Ô','À',1}, {'Ô','à',7}, {'Ô','å',2}, {'Ô','î',1}, {'Ô','ð',16}, {'Ô','ô',2}, {'Õ','Ô',1}, {'Õ','à',8}, {'Õ','â',1}, {'Õ','å',3}, {'Õ','î',20}, {'Õ','ð',14}, {'Ö','à',7}, {'Ö','è',3},
++{'Ö','ñ',1}, {'Ö','ò',1}, {'Ö','û',1}, {'×','å',46}, {'×','ò',60}, {'×','ó',8}, {'×','ü',1}, {'Ø','È',1}, {'Ø','à',3}, {'Ø','å',7}, {'Ø','è',8}, {'Ø','ò',8}, {'Ø','ó',7}, {'Ø','ø',1}, {'Ý','Ò',1}, {'Ý','á',5},
++{'Ý','â',1}, {'Ý','é',1}, {'Ý','ë',4}, {'Ý','í',1}, {'Ý','ð',10}, {'Ý','ñ',1}, {'Ý','ò',61}, {'Ý','õ',1}, {'Þ','ç',2}, {'Þ','ð',1}, {'ß','Í',1}, {'ß','á',4}, {'ß','â',49}, {'ß','ã',5}, {'ß','ä',8}, {'ß','å',9},
++{'ß','æ',2}, {'ß','ç',12}, {'ß','è',4}, {'ß','ê',10}, {'ß','ë',5}, {'ß','ì',6}, {'ß','í',28}, {'ß','î',24}, {'ß','ï',39}, {'ß','ð',4}, {'ß','ñ',30}, {'ß','ò',7}, {'ß','ó',5}, {'ß','õ',3}, {'ß','÷',4}, {'ß','ø',3},
++{'ß','ý',3}, {'à','À',56}, {'à','Á',18}, {'à','Â',48}, {'à','Ã',9}, {'à','Ä',26}, {'à','Å',12}, {'à','Æ',7}, {'à','Ç',11}, {'à','È',53}, {'à','Ê',59}, {'à','Ë',9}, {'à','Ì',21}, {'à','Í',32}, {'à','Î',27}, {'à','Ï',52},
++{'à','Ð',7}, {'à','Ñ',21}, {'à','Ò',42}, {'à','Ó',10}, {'à','Ô',1}, {'à','Õ',4}, {'à','×',16}, {'à','Ø',2}, {'à','Ý',7}, {'à','ß',22}, {'à','à',69}, {'à','á',257}, {'à','â',701}, {'à','ã',218}, {'à','ä',548}, {'à','å',328},
++{'à','æ',262}, {'à','ç',626}, {'à','è',225}, {'à','é',194}, {'à','ê',1285}, {'à','ë',1288}, {'à','ì',771}, {'à','í',911}, {'à','î',166}, {'à','ï',421}, {'à','ð',368}, {'à','ñ',802}, {'à','ò',874}, {'à','ó',98}, {'à','ô',38}, {'à','õ',228},
++{'à','ö',24}, {'à','÷',300}, {'à','ø',121}, {'à','ù',27}, {'à','ý',63}, {'à','þ',238}, {'à','ÿ',384}, {'á','À',1}, {'á','Ï',1}, {'á','Ô',1}, {'á','à',199}, {'á','á',6}, {'á','â',13}, {'á','ä',6}, {'á','å',420}, {'á','ç',2},
++{'á','è',142}, {'á','ê',7}, {'á','ë',137}, {'á','ì',7}, {'á','í',31}, {'á','î',327}, {'á','ï',4}, {'á','ð',172}, {'á','ñ',18}, {'á','ò',1}, {'á','ó',288}, {'á','õ',11}, {'á','ö',3}, {'á','÷',2}, {'á','ø',1}, {'á','ù',32},
++{'á','û',561}, {'á','ü',10}, {'á','ý',7}, {'á','þ',1}, {'á','ÿ',134}, {'â','À',11}, {'â','Á',1}, {'â','Â',6}, {'â','Ã',7}, {'â','Ä',5}, {'â','Å',5}, {'â','È',6}, {'â','Ê',14}, {'â','Ë',2}, {'â','Ì',9}, {'â','Í',13},
++{'â','Î',17}, {'â','Ï',51}, {'â','Ð',7}, {'â','Ñ',18}, {'â','Ò',4}, {'â','Ó',2}, {'â','Õ',8}, {'â','Ö',1}, {'â','×',2}, {'â','Ø',7}, {'â','Ý',2}, {'â','ß',4}, {'â','à',989}, {'â','á',26}, {'â','â',62}, {'â','ã',43},
++{'â','ä',74}, {'â','å',911}, {'â','æ',26}, {'â','ç',88}, {'â','è',387}, {'â','é',1}, {'â','ê',105}, {'â','ë',122}, {'â','ì',65}, {'â','í',231}, {'â','î',1220}, {'â','ï',118}, {'â','ð',97}, {'â','ñ',851}, {'â','ò',123}, {'â','ó',116},
++{'â','ô',1}, {'â','õ',9}, {'â','ö',11}, {'â','÷',47}, {'â','ø',53}, {'â','ù',2}, {'â','û',518}, {'â','ü',33}, {'â','ý',44}, {'â','þ',3}, {'â','ÿ',48}, {'ã','À',3}, {'ã','Á',1}, {'ã','Â',3}, {'ã','Ä',1}, {'ã','Ç',3},
++{'ã','È',3}, {'ã','Ë',4}, {'ã','Í',1}, {'ã','Î',4}, {'ã','Ï',3}, {'ã','Ð',1}, {'ã','Ñ',2}, {'ã','Ò',2}, {'ã','×',1}, {'ã','Ø',1}, {'ã','ß',2}, {'ã','à',210}, {'ã','á',18}, {'ã','â',6}, {'ã','ã',3}, {'ã','ä',191},
++{'ã','å',83}, {'ã','ç',2}, {'ã','è',103}, {'ã','ê',20}, {'ã','ë',205}, {'ã','ì',3}, {'ã','í',62}, {'ã','î',1340}, {'ã','ï',7}, {'ã','ð',180}, {'ã','ñ',13}, {'ã','ò',4}, {'ã','ó',88}, {'ã','õ',1}, {'ã','÷',15}, {'ã','ý',1},
++{'ã','ÿ',4}, {'ä','À',1}, {'ä','Â',2}, {'ä','Ä',2}, {'ä','Ç',1}, {'ä','È',2}, {'ä','Ê',1}, {'ä','Ì',1}, {'ä','Í',2}, {'ä','Ï',2}, {'ä','Ò',1}, {'ä','Ó',1}, {'ä','Ô',1}, {'ä','×',2}, {'ä','ß',2}, {'ä','à',795},
++{'ä','á',7}, {'ä','â',165}, {'ä','ã',6}, {'ä','ä',14}, {'ä','å',839}, {'ä','æ',8}, {'ä','ç',7}, {'ä','è',487}, {'ä','é',1}, {'ä','ê',79}, {'ä','ë',78}, {'ä','ì',10}, {'ä','í',289}, {'ä','î',613}, {'ä','ï',17}, {'ä','ð',195},
++{'ä','ñ',62}, {'ä','ò',41}, {'ä','ó',417}, {'ä','ô',1}, {'ä','õ',11}, {'ä','ö',138}, {'ä','÷',6}, {'ä','û',111}, {'ä','ü',200}, {'ä','ý',6}, {'ä','ÿ',68}, {'å','À',30}, {'å','Á',8}, {'å','Â',33}, {'å','Ã',7}, {'å','Ä',34},
++{'å','Å',13}, {'å','Æ',1}, {'å','Ç',6}, {'å','È',21}, {'å','Ê',21}, {'å','Ë',4}, {'å','Ì',13}, {'å','Í',20}, {'å','Î',21}, {'å','Ï',31}, {'å','Ð',2}, {'å','Ñ',13}, {'å','Ò',10}, {'å','Ó',7}, {'å','Ô',4}, {'å','Õ',2},
++{'å','Ö',1}, {'å','×',9}, {'å','Ø',2}, {'å','Ý',11}, {'å','Þ',1}, {'å','ß',25}, {'å','à',44}, {'å','á',463}, {'å','â',704}, {'å','ã',592}, {'å','ä',613}, {'å','å',281}, {'å','æ',136}, {'å','ç',444}, {'å','è',177}, {'å','é',297},
++{'å','ê',398}, {'å','ë',926}, {'å','ì',982}, {'å','í',1512}, {'å','î',206}, {'å','ï',524}, {'å','ð',1231}, {'å','ñ',1082}, {'å','ò',1607}, {'å','ó',103}, {'å','ô',15}, {'å','õ',182}, {'å','ö',66}, {'å','÷',308}, {'å','ø',235}, {'å','ù',137},
++{'å','ý',79}, {'å','þ',24}, {'å','ÿ',87}, {'æ','Â',1}, {'æ','È',2}, {'æ','Í',1}, {'æ','Î',1}, {'æ','Ï',3}, {'æ','à',187}, {'æ','á',4}, {'æ','â',9}, {'æ','ã',1}, {'æ','ä',145}, {'æ','å',690}, {'æ','æ',4}, {'æ','ç',1},
++{'æ','è',214}, {'æ','ê',35}, {'æ','ë',1}, {'æ','ì',8}, {'æ','í',96}, {'æ','î',13}, {'æ','ï',14}, {'æ','ð',7}, {'æ','ñ',4}, {'æ','ò',8}, {'æ','ó',69}, {'æ','õ',1}, {'æ','÷',1}, {'æ','ü',6}, {'æ','ý',2}, {'æ','ÿ',7},
++{'ç','À',1}, {'ç','Â',1}, {'ç','Æ',1}, {'ç','È',2}, {'ç','Ê',1}, {'ç','Ì',4}, {'ç','Í',3}, {'ç','Î',1}, {'ç','Ï',8}, {'ç','Ñ',4}, {'ç','Ò',2}, {'ç','Ö',1}, {'ç','×',2}, {'ç','ß',2}, {'ç','à',889}, {'ç','á',39},
++{'ç','â',147}, {'ç','ã',67}, {'ç','ä',174}, {'ç','å',56}, {'ç','æ',18}, {'ç','ç',11}, {'ç','è',113}, {'ç','ê',16}, {'ç','ë',33}, {'ç','ì',64}, {'ç','í',302}, {'ç','î',109}, {'ç','ï',31}, {'ç','ð',53}, {'ç','ñ',21}, {'ç','ò',7},
++{'ç','ó',109}, {'ç','õ',3}, {'ç','÷',11}, {'ç','ø',4}, {'ç','û',71}, {'ç','ü',20}, {'ç','ý',4}, {'ç','ÿ',43}, {'è','À',39}, {'è','Á',10}, {'è','Â',46}, {'è','Ã',5}, {'è','Ä',25}, {'è','Å',11}, {'è','Æ',2}, {'è','Ç',1},
++{'è','È',30}, {'è','Ê',24}, {'è','Ë',5}, {'è','Ì',36}, {'è','Í',39}, {'è','Î',25}, {'è','Ï',49}, {'è','Ð',3}, {'è','Ñ',22}, {'è','Ò',25}, {'è','Ó',8}, {'è','Ô',3}, {'è','Õ',4}, {'è','Ö',2}, {'è','×',4}, {'è','Ø',3},
++{'è','Ý',7}, {'è','ß',28}, {'è','à',90}, {'è','á',242}, {'è','â',658}, {'è','ã',153}, {'è','ä',435}, {'è','å',280}, {'è','æ',87}, {'è','ç',388}, {'è','è',301}, {'è','é',216}, {'è','ê',492}, {'è','ë',888}, {'è','ì',573}, {'è','í',958},
++{'è','î',216}, {'è','ï',335}, {'è','ð',275}, {'è','ñ',668}, {'è','ò',949}, {'è','ó',108}, {'è','ô',17}, {'è','õ',324}, {'è','ö',123}, {'è','÷',363}, {'è','ø',134}, {'è','ù',36}, {'è','ý',39}, {'è','þ',22}, {'è','ÿ',234}, {'é','À',20},
++{'é','Á',6}, {'é','Â',20}, {'é','Ã',6}, {'é','Ä',16}, {'é','Å',6}, {'é','Ç',2}, {'é','È',22}, {'é','Ê',18}, {'é','Ë',2}, {'é','Ì',12}, {'é','Í',13}, {'é','Î',15}, {'é','Ï',13}, {'é','Ð',7}, {'é','Ñ',9}, {'é','Ò',15},
++{'é','Ó',3}, {'é','Ô',1}, {'é','Õ',1}, {'é','×',1}, {'é','Ý',7}, {'é','ß',8}, {'é','à',21}, {'é','á',36}, {'é','â',112}, {'é','ã',26}, {'é','ä',100}, {'é','å',15}, {'é','æ',21}, {'é','ç',30}, {'é','è',88}, {'é','é',1},
++{'é','ê',87}, {'é','ë',25}, {'é','ì',47}, {'é','í',117}, {'é','î',55}, {'é','ï',133}, {'é','ð',42}, {'é','ñ',162}, {'é','ò',131}, {'é','ó',17}, {'é','ô',6}, {'é','õ',7}, {'é','ö',11}, {'é','÷',69}, {'é','ø',20}, {'é','ù',2},
++{'é','ý',15}, {'é','ÿ',16}, {'ê','À',11}, {'ê','Â',13}, {'ê','Ã',7}, {'ê','Ä',4}, {'ê','Å',2}, {'ê','Æ',1}, {'ê','Ç',5}, {'ê','È',8}, {'ê','Ê',9}, {'ê','Ë',2}, {'ê','Ì',5}, {'ê','Í',20}, {'ê','Î',7}, {'ê','Ï',9},
++{'ê','Ñ',7}, {'ê','Ò',9}, {'ê','Ó',4}, {'ê','Õ',1}, {'ê','Ö',1}, {'ê','×',4}, {'ê','Ý',2}, {'ê','ß',8}, {'ê','à',1358}, {'ê','á',49}, {'ê','â',147}, {'ê','ã',15}, {'ê','ä',48}, {'ê','å',93}, {'ê','æ',22}, {'ê','ç',55},
++{'ê','è',678}, {'ê','ê',28}, {'ê','ë',81}, {'ê','ì',22}, {'ê','í',151}, {'ê','î',1337}, {'ê','ï',64}, {'ê','ð',273}, {'ê','ñ',131}, {'ê','ò',238}, {'ê','ó',384}, {'ê','ô',7}, {'ê','õ',10}, {'ê','ö',5}, {'ê','÷',31}, {'ê','ø',2},
++{'ê','û',2}, {'ê','ý',13}, {'ê','ÿ',25}, {'ë','À',20}, {'ë','Á',2}, {'ë','Â',25}, {'ë','Ã',5}, {'ë','Ä',14}, {'ë','Å',3}, {'ë','Æ',1}, {'ë','È',19}, {'ë','Ê',12}, {'ë','Ë',2}, {'ë','Ì',14}, {'ë','Í',31}, {'ë','Î',20},
++{'ë','Ï',20}, {'ë','Ð',1}, {'ë','Ñ',20}, {'ë','Ò',10}, {'ë','Ó',1}, {'ë','Ô',1}, {'ë','Õ',1}, {'ë','Ö',1}, {'ë','×',2}, {'ë','Ý',4}, {'ë','ß',19}, {'ë','à',758}, {'ë','á',34}, {'ë','â',102}, {'ë','ã',35}, {'ë','ä',55},
++{'ë','å',675}, {'ë','æ',33}, {'ë','ç',23}, {'ë','è',1300}, {'ë','é',1}, {'ë','ê',114}, {'ë','ë',21}, {'ë','ì',45}, {'ë','í',111}, {'ë','î',947}, {'ë','ï',83}, {'ë','ð',18}, {'ë','ñ',320}, {'ë','ò',61}, {'ë','ó',264}, {'ë','ô',5},
++{'ë','õ',7}, {'ë','ö',2}, {'ë','÷',66}, {'ë','ø',1}, {'ë','û',146}, {'ë','ü',642}, {'ë','ý',21}, {'ë','þ',159}, {'ë','ÿ',299}, {'ì','À',11}, {'ì','Á',2}, {'ì','Â',14}, {'ì','Ã',8}, {'ì','Ä',10}, {'ì','Å',1}, {'ì','Ç',5},
++{'ì','È',12}, {'ì','Ê',10}, {'ì','Ì',6}, {'ì','Í',23}, {'ì','Î',12}, {'ì','Ï',12}, {'ì','Ð',1}, {'ì','Ñ',9}, {'ì','Ò',16}, {'ì','Ó',2}, {'ì','Õ',2}, {'ì','×',3}, {'ì','Ø',1}, {'ì','Ý',1}, {'ì','ß',10}, {'ì','à',447},
++{'ì','á',69}, {'ì','â',119}, {'ì','ã',33}, {'ì','ä',41}, {'ì','å',838}, {'ì','æ',27}, {'ì','ç',38}, {'ì','è',484}, {'ì','é',2}, {'ì','ê',71}, {'ì','ë',74}, {'ì','ì',96}, {'ì','í',497}, {'ì','î',761}, {'ì','ï',132}, {'ì','ð',41},
++{'ì','ñ',137}, {'ì','ò',74}, {'ì','ó',411}, {'ì','ô',5}, {'ì','õ',13}, {'ì','ö',4}, {'ì','÷',46}, {'ì','ø',3}, {'ì','ù',3}, {'ì','û',207}, {'ì','ü',33}, {'ì','ý',17}, {'ì','ÿ',82}, {'í','À',11}, {'í','Â',5}, {'í','Ã',8},
++{'í','Ä',3}, {'í','Å',4}, {'í','È',7}, {'í','Ê',10}, {'í','Ë',1}, {'í','Ì',2}, {'í','Í',6}, {'í','Î',2}, {'í','Ï',9}, {'í','Ð',2}, {'í','Ñ',2}, {'í','Ò',6}, {'í','Ó',1}, {'í','Ö',1}, {'í','×',6}, {'í','Ø',1},
++{'í','Ý',2}, {'í','ß',2}, {'í','à',1706}, {'í','á',28}, {'í','â',55}, {'í','ã',48}, {'í','ä',97}, {'í','å',1870}, {'í','æ',5}, {'í','ç',20}, {'í','è',1319}, {'í','ê',84}, {'í','ë',7}, {'í','ì',11}, {'í','í',331}, {'í','î',1483},
++{'í','ï',54}, {'í','ð',27}, {'í','ñ',133}, {'í','ò',165}, {'í','ó',480}, {'í','ô',14}, {'í','õ',4}, {'í','ö',65}, {'í','÷',55}, {'í','ø',3}, {'í','ù',31}, {'í','û',385}, {'í','ü',196}, {'í','ý',7}, {'í','þ',30}, {'í','ÿ',396},
++{'î','À',34}, {'î','Á',5}, {'î','Â',44}, {'î','Ã',9}, {'î','Ä',16}, {'î','Å',16}, {'î','Æ',4}, {'î','Ç',17}, {'î','È',43}, {'î','Ê',17}, {'î','Ë',3}, {'î','Ì',23}, {'î','Í',49}, {'î','Î',23}, {'î','Ï',40}, {'î','Ð',6},
++{'î','Ñ',14}, {'î','Ò',19}, {'î','Ó',4}, {'î','Ô',7}, {'î','Õ',4}, {'î','Ö',2}, {'î','×',12}, {'î','Ý',5}, {'î','ß',27}, {'î','à',34}, {'î','á',652}, {'î','â',1590}, {'î','ã',915}, {'î','ä',946}, {'î','å',457}, {'î','æ',437},
++{'î','ç',279}, {'î','è',291}, {'î','é',602}, {'î','ê',498}, {'î','ë',923}, {'î','ì',1073}, {'î','í',1281}, {'î','î',241}, {'î','ï',588}, {'î','ð',921}, {'î','ñ',1264}, {'î','ò',1536}, {'î','ó',152}, {'î','ô',35}, {'î','õ',137}, {'î','ö',51},
++{'î','÷',459}, {'î','ø',223}, {'î','ù',56}, {'î','ý',89}, {'î','þ',81}, {'î','ÿ',154}, {'ï','Â',2}, {'ï','È',1}, {'ï','Ê',1}, {'ï','à',252}, {'ï','â',1}, {'ï','ä',1}, {'ï','å',364}, {'ï','è',356}, {'ï','ê',8}, {'ï','ë',189},
++{'ï','í',11}, {'ï','î',1543}, {'ï','ï',6}, {'ï','ð',845}, {'ï','ñ',6}, {'ï','ò',23}, {'ï','ó',202}, {'ï','÷',2}, {'ï','û',33}, {'ï','ü',122}, {'ï','ÿ',149}, {'ð','À',3}, {'ð','Á',4}, {'ð','Â',3}, {'ð','Ä',6}, {'ð','Å',2},
++{'ð','Ç',1}, {'ð','Ê',2}, {'ð','Ì',1}, {'ð','Í',5}, {'ð','Î',8}, {'ð','Ï',10}, {'ð','Ñ',2}, {'ð','Ò',4}, {'ð','Ô',2}, {'ð','Õ',1}, {'ð','×',2}, {'ð','Ý',3}, {'ð','ß',6}, {'ð','à',1264}, {'ð','á',29}, {'ð','â',98},
++{'ð','ã',53}, {'ð','ä',129}, {'ð','å',936}, {'ð','æ',41}, {'ð','ç',20}, {'ð','è',777}, {'ð','é',1}, {'ð','ê',54}, {'ð','ë',29}, {'ð','ì',39}, {'ð','í',181}, {'ð','î',1126}, {'ð','ï',38}, {'ð','ð',34}, {'ð','ñ',76}, {'ð','ò',122},
++{'ð','ó',355}, {'ð','ô',8}, {'ð','õ',36}, {'ð','ö',23}, {'ð','÷',9}, {'ð','ø',49}, {'ð','ù',5}, {'ð','û',169}, {'ð','ü',98}, {'ð','ý',7}, {'ð','þ',59}, {'ð','ÿ',159}, {'ñ','À',6}, {'ñ','Â',4}, {'ñ','Ã',2}, {'ñ','Ä',2},
++{'ñ','Æ',1}, {'ñ','Ç',1}, {'ñ','È',4}, {'ñ','Ê',2}, {'ñ','Ë',3}, {'ñ','Ì',4}, {'ñ','Í',5}, {'ñ','Î',3}, {'ñ','Ï',3}, {'ñ','Ð',1}, {'ñ','Ñ',2}, {'ñ','Ò',1}, {'ñ','×',1}, {'ñ','Ý',3}, {'ñ','ß',1}, {'ñ','à',306},
++{'ñ','á',33}, {'ñ','â',259}, {'ñ','ã',18}, {'ñ','ä',41}, {'ñ','å',973}, {'ñ','æ',8}, {'ñ','ç',11}, {'ñ','è',295}, {'ñ','ê',691}, {'ñ','ë',519}, {'ñ','ì',222}, {'ñ','í',236}, {'ñ','î',403}, {'ñ','ï',274}, {'ñ','ð',58}, {'ñ','ñ',222},
++{'ñ','ò',1721}, {'ñ','ó',146}, {'ñ','ô',18}, {'ñ','õ',34}, {'ñ','ö',6}, {'ñ','÷',48}, {'ñ','ø',20}, {'ñ','ù',2}, {'ñ','û',79}, {'ñ','ü',401}, {'ñ','ý',10}, {'ñ','þ',27}, {'ñ','ÿ',680}, {'ò','À',29}, {'ò','Á',5}, {'ò','Â',25},
++{'ò','Ã',3}, {'ò','Ä',11}, {'ò','Å',9}, {'ò','Æ',2}, {'ò','Ç',5}, {'ò','È',16}, {'ò','Ê',21}, {'ò','Ì',20}, {'ò','Í',24}, {'ò','Î',10}, {'ò','Ï',23}, {'ò','Ð',2}, {'ò','Ñ',10}, {'ò','Ò',13}, {'ò','Ó',2}, {'ò','Ô',2},
++{'ò','Õ',3}, {'ò','×',11}, {'ò','Ø',1}, {'ò','Ý',3}, {'ò','Þ',1}, {'ò','ß',16}, {'ò','à',1225}, {'ò','á',56}, {'ò','â',436}, {'ò','ã',25}, {'ò','ä',67}, {'ò','å',864}, {'ò','æ',17}, {'ò','ç',33}, {'ò','è',758}, {'ò','é',1},
++{'ò','ê',180}, {'ò','ë',52}, {'ò','ì',65}, {'ò','í',329}, {'ò','î',2625}, {'ò','ï',119}, {'ò','ð',616}, {'ò','ñ',337}, {'ò','ò',117}, {'ò','ó',398}, {'ò','ô',1}, {'ò','õ',24}, {'ò','ö',10}, {'ò','÷',87}, {'ò','ø',3}, {'ò','ù',4},
++{'ò','û',398}, {'ò','ü',1292}, {'ò','ý',37}, {'ò','þ',13}, {'ò','ÿ',103}, {'ó','À',22}, {'ó','Á',4}, {'ó','Â',19}, {'ó','Ã',4}, {'ó','Ä',20}, {'ó','Å',3}, {'ó','Ç',2}, {'ó','È',25}, {'ó','Ê',9}, {'ó','Ë',6}, {'ó','Ì',12},
++{'ó','Í',18}, {'ó','Î',13}, {'ó','Ï',18}, {'ó','Ð',1}, {'ó','Ñ',18}, {'ó','Ò',12}, {'ó','Ó',2}, {'ó','Ô',2}, {'ó','Õ',5}, {'ó','×',6}, {'ó','Ø',3}, {'ó','Ý',5}, {'ó','ß',17}, {'ó','à',45}, {'ó','á',142}, {'ó','â',184},
++{'ó','ã',161}, {'ó','ä',445}, {'ó','å',83}, {'ó','æ',269}, {'ó','ç',75}, {'ó','è',111}, {'ó','é',40}, {'ó','ê',169}, {'ó','ë',251}, {'ó','ì',322}, {'ó','í',195}, {'ó','î',50}, {'ó','ï',258}, {'ó','ð',203}, {'ó','ñ',380}, {'ó','ò',442},
++{'ó','ó',24}, {'ó','ô',5}, {'ó','õ',97}, {'ó','÷',241}, {'ó','ø',302}, {'ó','ù',37}, {'ó','ý',19}, {'ó','þ',156}, {'ó','ÿ',31}, {'ô','Ï',1}, {'ô','Ö',2}, {'ô','à',21}, {'ô','å',45}, {'ô','è',47}, {'ô','ë',14}, {'ô','í',2},
++{'ô','î',24}, {'ô','ð',20}, {'ô','ñ',4}, {'ô','ò',5}, {'ô','ó',8}, {'ô','ô',6}, {'ô','û',4}, {'ô','ü',1}, {'ô','þ',2}, {'õ','À',7}, {'õ','Â',7}, {'õ','Ä',4}, {'õ','Å',3}, {'õ','Æ',3}, {'õ','È',5}, {'õ','Ê',4},
++{'õ','Ë',2}, {'õ','Ì',1}, {'õ','Í',10}, {'õ','Î',5}, {'õ','Ï',7}, {'õ','Ñ',4}, {'õ','Ò',5}, {'õ','×',3}, {'õ','Ø',5}, {'õ','Ý',2}, {'õ','ß',5}, {'õ','à',135}, {'õ','á',21}, {'õ','â',72}, {'õ','ã',27}, {'õ','ä',21},
++{'õ','å',25}, {'õ','æ',4}, {'õ','ç',13}, {'õ','è',75}, {'õ','ê',16}, {'õ','ë',43}, {'õ','ì',49}, {'õ','í',83}, {'õ','î',450}, {'õ','ï',70}, {'õ','ð',19}, {'õ','ñ',39}, {'õ','ò',27}, {'õ','ó',42}, {'õ','ô',3}, {'õ','õ',4},
++{'õ','ö',2}, {'õ','÷',18}, {'õ','ø',8}, {'õ','û',1}, {'õ','ü',6}, {'õ','ý',7}, {'õ','ÿ',9}, {'ö','À',1}, {'ö','Ã',1}, {'ö','È',2}, {'ö','Ê',1}, {'ö','Ë',1}, {'ö','Ì',1}, {'ö','Ï',1}, {'ö','Ò',1}, {'ö','ß',1},
++{'ö','à',179}, {'ö','á',1}, {'ö','â',11}, {'ö','å',162}, {'ö','ç',5}, {'ö','è',75}, {'ö','ê',6}, {'ö','ë',5}, {'ö','ì',1}, {'ö','í',2}, {'ö','î',29}, {'ö','ï',4}, {'ö','ð',2}, {'ö','ñ',4}, {'ö','ò',4}, {'ö','ó',41},
++{'ö','÷',2}, {'ö','û',41}, {'÷','Â',1}, {'÷','Ã',1}, {'÷','Å',1}, {'÷','È',1}, {'÷','Í',3}, {'÷','Î',1}, {'÷','Ñ',3}, {'÷','Ò',2}, {'÷','×',1}, {'÷','à',294}, {'÷','á',1}, {'÷','â',6}, {'÷','å',874}, {'÷','è',319},
++{'÷','ê',76}, {'÷','ë',9}, {'÷','ì',4}, {'÷','í',158}, {'÷','î',10}, {'÷','ï',3}, {'÷','ð',8}, {'÷','ñ',2}, {'÷','ò',536}, {'÷','ó',101}, {'÷','÷',7}, {'÷','ø',37}, {'÷','ü',49}, {'÷','ÿ',1}, {'ø','ß',1}, {'ø','à',150},
++{'ø','â',5}, {'ø','å',388}, {'ø','è',188}, {'ø','ê',201}, {'ø','ë',68}, {'ø','ì',4}, {'ø','í',78}, {'ø','î',53}, {'ø','ï',8}, {'ø','ð',3}, {'ø','ñ',2}, {'ø','ò',17}, {'ø','ó',45}, {'ø','õ',1}, {'ø','ö',1}, {'ø','ü',200},
++{'ø','ý',1}, {'ù','Ï',1}, {'ù','Ò',1}, {'ù','à',68}, {'ù','å',242}, {'ù','è',96}, {'ù','í',7}, {'ù','î',1}, {'ù','ð',1}, {'ù','ñ',3}, {'ù','ó',20}, {'ù','ü',2}, {'û','À',14}, {'û','Á',6}, {'û','Â',28}, {'û','Ã',4},
++{'û','Ä',8}, {'û','Å',6}, {'û','Æ',1}, {'û','Ç',4}, {'û','È',12}, {'û','Ê',10}, {'û','Ë',2}, {'û','Ì',15}, {'û','Í',16}, {'û','Î',13}, {'û','Ï',6}, {'û','Ð',3}, {'û','Ñ',7}, {'û','Ò',10}, {'û','Ó',1}, {'û','Ô',1},
++{'û','×',4}, {'û','Ý',5}, {'û','Þ',1}, {'û','ß',10}, {'û','à',10}, {'û','á',66}, {'û','â',248}, {'û','ã',37}, {'û','ä',65}, {'û','å',138}, {'û','æ',21}, {'û','ç',45}, {'û','è',74}, {'û','é',264}, {'û','ê',83}, {'û','ë',305},
++{'û','ì',164}, {'û','í',141}, {'û','î',34}, {'û','ï',231}, {'û','ð',61}, {'û','ñ',189}, {'û','ò',148}, {'û','ó',35}, {'û','ô',1}, {'û','õ',146}, {'û','ö',3}, {'û','÷',63}, {'û','ø',80}, {'û','ù',1}, {'û','ý',9}, {'û','ÿ',10},
++{'ü','À',44}, {'ü','Á',8}, {'ü','Â',45}, {'ü','Ã',7}, {'ü','Ä',18}, {'ü','Å',12}, {'ü','Æ',1}, {'ü','Ç',4}, {'ü','È',29}, {'ü','Ê',19}, {'ü','Ì',18}, {'ü','Í',36}, {'ü','Î',25}, {'ü','Ï',42}, {'ü','Ð',4}, {'ü','Ñ',25},
++{'ü','Ò',24}, {'ü','Ó',10}, {'ü','Ô',1}, {'ü','Õ',3}, {'ü','×',14}, {'ü','Ø',1}, {'ü','Ý',4}, {'ü','ß',21}, {'ü','à',38}, {'ü','á',63}, {'ü','â',196}, {'ü','ã',35}, {'ü','ä',110}, {'ü','å',154}, {'ü','æ',11}, {'ü','ç',58},
++{'ü','è',194}, {'ü','é',1}, {'ü','ê',324}, {'ü','ë',36}, {'ü','ì',124}, {'ü','í',321}, {'ü','î',92}, {'ü','ï',164}, {'ü','ð',41}, {'ü','ñ',302}, {'ü','ò',152}, {'ü','ó',48}, {'ü','ô',5}, {'ü','õ',20}, {'ü','ö',15}, {'ü','÷',88},
++{'ü','ø',85}, {'ü','ù',1}, {'ü','ý',27}, {'ü','þ',88}, {'ü','ÿ',110}, {'ý','È',1}, {'ý','á',1}, {'ý','ã',1}, {'ý','ä',2}, {'ý','ç',1}, {'ý','ê',6}, {'ý','ë',9}, {'ý','ì',6}, {'ý','í',3}, {'ý','ð',8}, {'ý','ñ',4},
++{'ý','ò',531}, {'ý','ô',2}, {'ý','õ',1}, {'ý','ý',2}, {'þ','À',10}, {'þ','Á',1}, {'þ','Â',10}, {'þ','Ã',1}, {'þ','Ä',3}, {'þ','Æ',1}, {'þ','Ç',2}, {'þ','È',17}, {'þ','Ê',4}, {'þ','Ë',2}, {'þ','Ì',3}, {'þ','Í',16},
++{'þ','Î',4}, {'þ','Ï',5}, {'þ','Ð',1}, {'þ','Ñ',3}, {'þ','Ò',2}, {'þ','Ó',2}, {'þ','Õ',3}, {'þ','×',1}, {'þ','Ø',2}, {'þ','Ý',2}, {'þ','ß',9}, {'þ','à',15}, {'þ','á',73}, {'þ','â',37}, {'þ','ã',12}, {'þ','ä',72},
++{'þ','å',12}, {'þ','æ',6}, {'þ','ç',11}, {'þ','è',45}, {'þ','é',1}, {'þ','ê',30}, {'þ','ë',9}, {'þ','ì',28}, {'þ','í',58}, {'þ','î',17}, {'þ','ï',58}, {'þ','ð',14}, {'þ','ñ',71}, {'þ','ò',139}, {'þ','ó',10}, {'þ','ô',5},
++{'þ','õ',5}, {'þ','ö',3}, {'þ','÷',42}, {'þ','ø',11}, {'þ','ù',27}, {'þ','ý',2}, {'þ','þ',10}, {'þ','ÿ',20}, {'ÿ','À',18}, {'ÿ','Á',5}, {'ÿ','Â',28}, {'ÿ','Ã',2}, {'ÿ','Ä',23}, {'ÿ','Å',6}, {'ÿ','Æ',2}, {'ÿ','Ç',3},
++{'ÿ','È',15}, {'ÿ','Ê',14}, {'ÿ','Ë',3}, {'ÿ','Ì',10}, {'ÿ','Í',23}, {'ÿ','Î',22}, {'ÿ','Ï',26}, {'ÿ','Ð',5}, {'ÿ','Ñ',12}, {'ÿ','Ò',17}, {'ÿ','Ó',1}, {'ÿ','Õ',2}, {'ÿ','Ö',1}, {'ÿ','×',7}, {'ÿ','Ø',2}, {'ÿ','Ý',10},
++{'ÿ','ß',18}, {'ÿ','à',23}, {'ÿ','á',85}, {'ÿ','â',260}, {'ÿ','ã',44}, {'ÿ','ä',170}, {'ÿ','å',70}, {'ÿ','æ',63}, {'ÿ','ç',114}, {'ÿ','è',146}, {'ÿ','é',7}, {'ÿ','ê',145}, {'ÿ','ë',137}, {'ÿ','ì',108}, {'ÿ','í',275}, {'ÿ','î',110},
++{'ÿ','ï',204}, {'ÿ','ð',43}, {'ÿ','ñ',259}, {'ÿ','ò',457}, {'ÿ','ó',64}, {'ÿ','ô',2}, {'ÿ','õ',37}, {'ÿ','ö',18}, {'ÿ','÷',75}, {'ÿ','ø',13}, {'ÿ','ù',33}, {'ÿ','ý',22}, {'ÿ','þ',31}, {'ÿ','ÿ',47}
++};
++
++static const lng_stat koi[]={
++{'À','À',10}, {'À','Á',15}, {'À','Â',73}, {'À','Ã',3}, {'À','Ä',72}, {'À','Å',12}, {'À','Æ',5}, {'À','Ç',12}, {'À','È',5}, {'À','É',45}, {'À','Ê',1}, {'À','Ë',30}, {'À','Ì',9}, {'À','Í',28}, {'À','Î',58}, {'À','Ï',17},
++{'À','Ð',58}, {'À','Ñ',20}, {'À','Ò',14}, {'À','Ó',71}, {'À','Ô',139}, {'À','Õ',10}, {'À','Ö',6}, {'À','×',37}, {'À','Ú',11}, {'À','Û',11}, {'À','Ü',2}, {'À','Ý',27}, {'À','Þ',42}, {'À','á',10}, {'À','â',1}, {'À','ä',3},
++{'À','ç',1}, {'À','è',3}, {'À','é',17}, {'À','ë',4}, {'À','ì',2}, {'À','í',3}, {'À','î',16}, {'À','ï',4}, {'À','ð',5}, {'À','ñ',9}, {'À','ò',1}, {'À','ó',3}, {'À','ô',2}, {'À','õ',2}, {'À','ö',1}, {'À','÷',10},
++{'À','ú',2}, {'À','û',2}, {'À','ü',2}, {'À','þ',1}, {'Á','À',238}, {'Á','Á',69}, {'Á','Â',257}, {'Á','Ã',24}, {'Á','Ä',548}, {'Á','Å',328}, {'Á','Æ',38}, {'Á','Ç',218}, {'Á','È',228}, {'Á','É',225}, {'Á','Ê',194}, {'Á','Ë',1285},
++{'Á','Ì',1288}, {'Á','Í',771}, {'Á','Î',911}, {'Á','Ï',166}, {'Á','Ð',421}, {'Á','Ñ',384}, {'Á','Ò',368}, {'Á','Ó',802}, {'Á','Ô',874}, {'Á','Õ',98}, {'Á','Ö',262}, {'Á','×',701}, {'Á','Ú',626}, {'Á','Û',121}, {'Á','Ü',63}, {'Á','Ý',27},
++{'Á','Þ',300}, {'Á','á',56}, {'Á','â',18}, {'Á','ä',26}, {'Á','å',12}, {'Á','æ',1}, {'Á','ç',9}, {'Á','è',4}, {'Á','é',53}, {'Á','ë',59}, {'Á','ì',9}, {'Á','í',21}, {'Á','î',32}, {'Á','ï',27}, {'Á','ð',52}, {'Á','ñ',22},
++{'Á','ò',7}, {'Á','ó',21}, {'Á','ô',42}, {'Á','õ',10}, {'Á','ö',7}, {'Á','÷',48}, {'Á','ú',11}, {'Á','û',2}, {'Á','ü',7}, {'Á','þ',16}, {'Â','À',1}, {'Â','Á',199}, {'Â','Â',6}, {'Â','Ã',3}, {'Â','Ä',6}, {'Â','Å',420},
++{'Â','È',11}, {'Â','É',142}, {'Â','Ë',7}, {'Â','Ì',137}, {'Â','Í',7}, {'Â','Î',31}, {'Â','Ï',327}, {'Â','Ð',4}, {'Â','Ñ',134}, {'Â','Ò',172}, {'Â','Ó',18}, {'Â','Ô',1}, {'Â','Õ',288}, {'Â','×',13}, {'Â','Ø',10}, {'Â','Ù',561},
++{'Â','Ú',2}, {'Â','Û',1}, {'Â','Ü',7}, {'Â','Ý',32}, {'Â','Þ',2}, {'Â','á',1}, {'Â','æ',1}, {'Â','ð',1}, {'Ã','Á',179}, {'Ã','Â',1}, {'Ã','Å',162}, {'Ã','É',75}, {'Ã','Ë',6}, {'Ã','Ì',5}, {'Ã','Í',1}, {'Ã','Î',2},
++{'Ã','Ï',29}, {'Ã','Ð',4}, {'Ã','Ò',2}, {'Ã','Ó',4}, {'Ã','Ô',4}, {'Ã','Õ',41}, {'Ã','×',11}, {'Ã','Ù',41}, {'Ã','Ú',5}, {'Ã','Þ',2}, {'Ã','á',1}, {'Ã','ç',1}, {'Ã','é',2}, {'Ã','ë',1}, {'Ã','ì',1}, {'Ã','í',1},
++{'Ã','ð',1}, {'Ã','ñ',1}, {'Ã','ô',1}, {'Ä','Á',795}, {'Ä','Â',7}, {'Ä','Ã',138}, {'Ä','Ä',14}, {'Ä','Å',839}, {'Ä','Æ',1}, {'Ä','Ç',6}, {'Ä','È',11}, {'Ä','É',487}, {'Ä','Ê',1}, {'Ä','Ë',79}, {'Ä','Ì',78}, {'Ä','Í',10},
++{'Ä','Î',289}, {'Ä','Ï',613}, {'Ä','Ð',17}, {'Ä','Ñ',68}, {'Ä','Ò',195}, {'Ä','Ó',62}, {'Ä','Ô',41}, {'Ä','Õ',417}, {'Ä','Ö',8}, {'Ä','×',165}, {'Ä','Ø',200}, {'Ä','Ù',111}, {'Ä','Ú',7}, {'Ä','Ü',6}, {'Ä','Þ',6}, {'Ä','á',1},
++{'Ä','ä',2}, {'Ä','æ',1}, {'Ä','é',2}, {'Ä','ë',1}, {'Ä','í',1}, {'Ä','î',2}, {'Ä','ð',2}, {'Ä','ñ',2}, {'Ä','ô',1}, {'Ä','õ',1}, {'Ä','÷',2}, {'Ä','ú',1}, {'Ä','þ',2}, {'Å','À',24}, {'Å','Á',44}, {'Å','Â',463},
++{'Å','Ã',66}, {'Å','Ä',613}, {'Å','Å',281}, {'Å','Æ',15}, {'Å','Ç',592}, {'Å','È',182}, {'Å','É',177}, {'Å','Ê',297}, {'Å','Ë',398}, {'Å','Ì',926}, {'Å','Í',982}, {'Å','Î',1512}, {'Å','Ï',206}, {'Å','Ð',524}, {'Å','Ñ',87}, {'Å','Ò',1231},
++{'Å','Ó',1082}, {'Å','Ô',1607}, {'Å','Õ',103}, {'Å','Ö',136}, {'Å','×',704}, {'Å','Ú',444}, {'Å','Û',235}, {'Å','Ü',79}, {'Å','Ý',137}, {'Å','Þ',308}, {'Å','à',1}, {'Å','á',30}, {'Å','â',8}, {'Å','ã',1}, {'Å','ä',34}, {'Å','å',13},
++{'Å','æ',4}, {'Å','ç',7}, {'Å','è',2}, {'Å','é',21}, {'Å','ë',21}, {'Å','ì',4}, {'Å','í',13}, {'Å','î',20}, {'Å','ï',21}, {'Å','ð',31}, {'Å','ñ',25}, {'Å','ò',2}, {'Å','ó',13}, {'Å','ô',10}, {'Å','õ',7}, {'Å','ö',1},
++{'Å','÷',33}, {'Å','ú',6}, {'Å','û',2}, {'Å','ü',11}, {'Å','þ',9}, {'Æ','À',2}, {'Æ','Á',21}, {'Æ','Å',45}, {'Æ','Æ',6}, {'Æ','É',47}, {'Æ','Ì',14}, {'Æ','Î',2}, {'Æ','Ï',24}, {'Æ','Ò',20}, {'Æ','Ó',4}, {'Æ','Ô',5},
++{'Æ','Õ',8}, {'Æ','Ø',1}, {'Æ','Ù',4}, {'Æ','ã',2}, {'Æ','ð',1}, {'Ç','Á',210}, {'Ç','Â',18}, {'Ç','Ä',191}, {'Ç','Å',83}, {'Ç','Ç',3}, {'Ç','È',1}, {'Ç','É',103}, {'Ç','Ë',20}, {'Ç','Ì',205}, {'Ç','Í',3}, {'Ç','Î',62},
++{'Ç','Ï',1340}, {'Ç','Ð',7}, {'Ç','Ñ',4}, {'Ç','Ò',180}, {'Ç','Ó',13}, {'Ç','Ô',4}, {'Ç','Õ',88}, {'Ç','×',6}, {'Ç','Ú',2}, {'Ç','Ü',1}, {'Ç','Þ',15}, {'Ç','á',3}, {'Ç','â',1}, {'Ç','ä',1}, {'Ç','é',3}, {'Ç','ì',4},
++{'Ç','î',1}, {'Ç','ï',4}, {'Ç','ð',3}, {'Ç','ñ',2}, {'Ç','ò',1}, {'Ç','ó',2}, {'Ç','ô',2}, {'Ç','÷',3}, {'Ç','ú',3}, {'Ç','û',1}, {'Ç','þ',1}, {'È','Á',135}, {'È','Â',21}, {'È','Ã',2}, {'È','Ä',21}, {'È','Å',25},
++{'È','Æ',3}, {'È','Ç',27}, {'È','È',4}, {'È','É',75}, {'È','Ë',16}, {'È','Ì',43}, {'È','Í',49}, {'È','Î',83}, {'È','Ï',450}, {'È','Ð',70}, {'È','Ñ',9}, {'È','Ò',19}, {'È','Ó',39}, {'È','Ô',27}, {'È','Õ',42}, {'È','Ö',4},
++{'È','×',72}, {'È','Ø',6}, {'È','Ù',1}, {'È','Ú',13}, {'È','Û',8}, {'È','Ü',7}, {'È','Þ',18}, {'È','á',7}, {'È','ä',4}, {'È','å',3}, {'È','é',5}, {'È','ë',4}, {'È','ì',2}, {'È','í',1}, {'È','î',10}, {'È','ï',5},
++{'È','ð',7}, {'È','ñ',5}, {'È','ó',4}, {'È','ô',5}, {'È','ö',3}, {'È','÷',7}, {'È','û',5}, {'È','ü',2}, {'È','þ',3}, {'É','À',22}, {'É','Á',90}, {'É','Â',242}, {'É','Ã',123}, {'É','Ä',435}, {'É','Å',280}, {'É','Æ',17},
++{'É','Ç',153}, {'É','È',324}, {'É','É',301}, {'É','Ê',216}, {'É','Ë',492}, {'É','Ì',888}, {'É','Í',573}, {'É','Î',958}, {'É','Ï',216}, {'É','Ð',335}, {'É','Ñ',234}, {'É','Ò',275}, {'É','Ó',668}, {'É','Ô',949}, {'É','Õ',108}, {'É','Ö',87},
++{'É','×',658}, {'É','Ú',388}, {'É','Û',134}, {'É','Ü',39}, {'É','Ý',36}, {'É','Þ',363}, {'É','á',39}, {'É','â',10}, {'É','ã',2}, {'É','ä',25}, {'É','å',11}, {'É','æ',3}, {'É','ç',5}, {'É','è',4}, {'É','é',30}, {'É','ë',24},
++{'É','ì',5}, {'É','í',36}, {'É','î',39}, {'É','ï',25}, {'É','ð',49}, {'É','ñ',28}, {'É','ò',3}, {'É','ó',22}, {'É','ô',25}, {'É','õ',8}, {'É','ö',2}, {'É','÷',46}, {'É','ú',1}, {'É','û',3}, {'É','ü',7}, {'É','þ',4},
++{'Ê','Á',21}, {'Ê','Â',36}, {'Ê','Ã',11}, {'Ê','Ä',100}, {'Ê','Å',15}, {'Ê','Æ',6}, {'Ê','Ç',26}, {'Ê','È',7}, {'Ê','É',88}, {'Ê','Ê',1}, {'Ê','Ë',87}, {'Ê','Ì',25}, {'Ê','Í',47}, {'Ê','Î',117}, {'Ê','Ï',55}, {'Ê','Ð',133},
++{'Ê','Ñ',16}, {'Ê','Ò',42}, {'Ê','Ó',162}, {'Ê','Ô',131}, {'Ê','Õ',17}, {'Ê','Ö',21}, {'Ê','×',112}, {'Ê','Ú',30}, {'Ê','Û',20}, {'Ê','Ü',15}, {'Ê','Ý',2}, {'Ê','Þ',69}, {'Ê','á',20}, {'Ê','â',6}, {'Ê','ä',16}, {'Ê','å',6},
++{'Ê','æ',1}, {'Ê','ç',6}, {'Ê','è',1}, {'Ê','é',22}, {'Ê','ë',18}, {'Ê','ì',2}, {'Ê','í',12}, {'Ê','î',13}, {'Ê','ï',15}, {'Ê','ð',13}, {'Ê','ñ',8}, {'Ê','ò',7}, {'Ê','ó',9}, {'Ê','ô',15}, {'Ê','õ',3}, {'Ê','÷',20},
++{'Ê','ú',2}, {'Ê','ü',7}, {'Ê','þ',1}, {'Ë','Á',1358}, {'Ë','Â',49}, {'Ë','Ã',5}, {'Ë','Ä',48}, {'Ë','Å',93}, {'Ë','Æ',7}, {'Ë','Ç',15}, {'Ë','È',10}, {'Ë','É',678}, {'Ë','Ë',28}, {'Ë','Ì',81}, {'Ë','Í',22}, {'Ë','Î',151},
++{'Ë','Ï',1337}, {'Ë','Ð',64}, {'Ë','Ñ',25}, {'Ë','Ò',273}, {'Ë','Ó',131}, {'Ë','Ô',238}, {'Ë','Õ',384}, {'Ë','Ö',22}, {'Ë','×',147}, {'Ë','Ù',2}, {'Ë','Ú',55}, {'Ë','Û',2}, {'Ë','Ü',13}, {'Ë','Þ',31}, {'Ë','á',11}, {'Ë','ã',1},
++{'Ë','ä',4}, {'Ë','å',2}, {'Ë','ç',7}, {'Ë','è',1}, {'Ë','é',8}, {'Ë','ë',9}, {'Ë','ì',2}, {'Ë','í',5}, {'Ë','î',20}, {'Ë','ï',7}, {'Ë','ð',9}, {'Ë','ñ',8}, {'Ë','ó',7}, {'Ë','ô',9}, {'Ë','õ',4}, {'Ë','ö',1},
++{'Ë','÷',13}, {'Ë','ú',5}, {'Ë','ü',2}, {'Ë','þ',4}, {'Ì','À',159}, {'Ì','Á',758}, {'Ì','Â',34}, {'Ì','Ã',2}, {'Ì','Ä',55}, {'Ì','Å',675}, {'Ì','Æ',5}, {'Ì','Ç',35}, {'Ì','È',7}, {'Ì','É',1300}, {'Ì','Ê',1}, {'Ì','Ë',114},
++{'Ì','Ì',21}, {'Ì','Í',45}, {'Ì','Î',111}, {'Ì','Ï',947}, {'Ì','Ð',83}, {'Ì','Ñ',299}, {'Ì','Ò',18}, {'Ì','Ó',320}, {'Ì','Ô',61}, {'Ì','Õ',264}, {'Ì','Ö',33}, {'Ì','×',102}, {'Ì','Ø',642}, {'Ì','Ù',146}, {'Ì','Ú',23}, {'Ì','Û',1},
++{'Ì','Ü',21}, {'Ì','Þ',66}, {'Ì','á',20}, {'Ì','â',2}, {'Ì','ã',1}, {'Ì','ä',14}, {'Ì','å',3}, {'Ì','æ',1}, {'Ì','ç',5}, {'Ì','è',1}, {'Ì','é',19}, {'Ì','ë',12}, {'Ì','ì',2}, {'Ì','í',14}, {'Ì','î',31}, {'Ì','ï',20},
++{'Ì','ð',20}, {'Ì','ñ',19}, {'Ì','ò',1}, {'Ì','ó',20}, {'Ì','ô',10}, {'Ì','õ',1}, {'Ì','ö',1}, {'Ì','÷',25}, {'Ì','ü',4}, {'Ì','þ',2}, {'Í','Á',447}, {'Í','Â',69}, {'Í','Ã',4}, {'Í','Ä',41}, {'Í','Å',838}, {'Í','Æ',5},
++{'Í','Ç',33}, {'Í','È',13}, {'Í','É',484}, {'Í','Ê',2}, {'Í','Ë',71}, {'Í','Ì',74}, {'Í','Í',96}, {'Í','Î',497}, {'Í','Ï',761}, {'Í','Ð',132}, {'Í','Ñ',82}, {'Í','Ò',41}, {'Í','Ó',137}, {'Í','Ô',74}, {'Í','Õ',411}, {'Í','Ö',27},
++{'Í','×',119}, {'Í','Ø',33}, {'Í','Ù',207}, {'Í','Ú',38}, {'Í','Û',3}, {'Í','Ü',17}, {'Í','Ý',3}, {'Í','Þ',46}, {'Í','á',11}, {'Í','â',2}, {'Í','ä',10}, {'Í','å',1}, {'Í','ç',8}, {'Í','è',2}, {'Í','é',12}, {'Í','ë',10},
++{'Í','í',6}, {'Í','î',23}, {'Í','ï',12}, {'Í','ð',12}, {'Í','ñ',10}, {'Í','ò',1}, {'Í','ó',9}, {'Í','ô',16}, {'Í','õ',2}, {'Í','÷',14}, {'Í','ú',5}, {'Í','û',1}, {'Í','ü',1}, {'Í','þ',3}, {'Î','À',30}, {'Î','Á',1706},
++{'Î','Â',28}, {'Î','Ã',65}, {'Î','Ä',97}, {'Î','Å',1870}, {'Î','Æ',14}, {'Î','Ç',48}, {'Î','È',4}, {'Î','É',1319}, {'Î','Ë',84}, {'Î','Ì',7}, {'Î','Í',11}, {'Î','Î',331}, {'Î','Ï',1483}, {'Î','Ð',54}, {'Î','Ñ',396}, {'Î','Ò',27},
++{'Î','Ó',133}, {'Î','Ô',165}, {'Î','Õ',480}, {'Î','Ö',5}, {'Î','×',55}, {'Î','Ø',196}, {'Î','Ù',385}, {'Î','Ú',20}, {'Î','Û',3}, {'Î','Ü',7}, {'Î','Ý',31}, {'Î','Þ',55}, {'Î','á',11}, {'Î','ã',1}, {'Î','ä',3}, {'Î','å',4},
++{'Î','ç',8}, {'Î','é',7}, {'Î','ë',10}, {'Î','ì',1}, {'Î','í',2}, {'Î','î',6}, {'Î','ï',2}, {'Î','ð',9}, {'Î','ñ',2}, {'Î','ò',2}, {'Î','ó',2}, {'Î','ô',6}, {'Î','õ',1}, {'Î','÷',5}, {'Î','û',1}, {'Î','ü',2},
++{'Î','þ',6}, {'Ï','À',81}, {'Ï','Á',34}, {'Ï','Â',652}, {'Ï','Ã',51}, {'Ï','Ä',946}, {'Ï','Å',457}, {'Ï','Æ',35}, {'Ï','Ç',915}, {'Ï','È',137}, {'Ï','É',291}, {'Ï','Ê',602}, {'Ï','Ë',498}, {'Ï','Ì',923}, {'Ï','Í',1073}, {'Ï','Î',1281},
++{'Ï','Ï',241}, {'Ï','Ð',588}, {'Ï','Ñ',154}, {'Ï','Ò',921}, {'Ï','Ó',1264}, {'Ï','Ô',1536}, {'Ï','Õ',152}, {'Ï','Ö',437}, {'Ï','×',1590}, {'Ï','Ú',279}, {'Ï','Û',223}, {'Ï','Ü',89}, {'Ï','Ý',56}, {'Ï','Þ',459}, {'Ï','á',34}, {'Ï','â',5},
++{'Ï','ã',2}, {'Ï','ä',16}, {'Ï','å',16}, {'Ï','æ',7}, {'Ï','ç',9}, {'Ï','è',4}, {'Ï','é',43}, {'Ï','ë',17}, {'Ï','ì',3}, {'Ï','í',23}, {'Ï','î',49}, {'Ï','ï',23}, {'Ï','ð',40}, {'Ï','ñ',27}, {'Ï','ò',6}, {'Ï','ó',14},
++{'Ï','ô',19}, {'Ï','õ',4}, {'Ï','ö',4}, {'Ï','÷',44}, {'Ï','ú',17}, {'Ï','ü',5}, {'Ï','þ',12}, {'Ð','Á',252}, {'Ð','Ä',1}, {'Ð','Å',364}, {'Ð','É',356}, {'Ð','Ë',8}, {'Ð','Ì',189}, {'Ð','Î',11}, {'Ð','Ï',1543}, {'Ð','Ð',6},
++{'Ð','Ñ',149}, {'Ð','Ò',845}, {'Ð','Ó',6}, {'Ð','Ô',23}, {'Ð','Õ',202}, {'Ð','×',1}, {'Ð','Ø',122}, {'Ð','Ù',33}, {'Ð','Þ',2}, {'Ð','é',1}, {'Ð','ë',1}, {'Ð','÷',2}, {'Ñ','À',31}, {'Ñ','Á',23}, {'Ñ','Â',85}, {'Ñ','Ã',18},
++{'Ñ','Ä',170}, {'Ñ','Å',70}, {'Ñ','Æ',2}, {'Ñ','Ç',44}, {'Ñ','È',37}, {'Ñ','É',146}, {'Ñ','Ê',7}, {'Ñ','Ë',145}, {'Ñ','Ì',137}, {'Ñ','Í',108}, {'Ñ','Î',275}, {'Ñ','Ï',110}, {'Ñ','Ð',204}, {'Ñ','Ñ',47}, {'Ñ','Ò',43}, {'Ñ','Ó',259},
++{'Ñ','Ô',457}, {'Ñ','Õ',64}, {'Ñ','Ö',63}, {'Ñ','×',260}, {'Ñ','Ú',114}, {'Ñ','Û',13}, {'Ñ','Ü',22}, {'Ñ','Ý',33}, {'Ñ','Þ',75}, {'Ñ','á',18}, {'Ñ','â',5}, {'Ñ','ã',1}, {'Ñ','ä',23}, {'Ñ','å',6}, {'Ñ','ç',2}, {'Ñ','è',2},
++{'Ñ','é',15}, {'Ñ','ë',14}, {'Ñ','ì',3}, {'Ñ','í',10}, {'Ñ','î',23}, {'Ñ','ï',22}, {'Ñ','ð',26}, {'Ñ','ñ',18}, {'Ñ','ò',5}, {'Ñ','ó',12}, {'Ñ','ô',17}, {'Ñ','õ',1}, {'Ñ','ö',2}, {'Ñ','÷',28}, {'Ñ','ú',3}, {'Ñ','û',2},
++{'Ñ','ü',10}, {'Ñ','þ',7}, {'Ò','À',59}, {'Ò','Á',1264}, {'Ò','Â',29}, {'Ò','Ã',23}, {'Ò','Ä',129}, {'Ò','Å',936}, {'Ò','Æ',8}, {'Ò','Ç',53}, {'Ò','È',36}, {'Ò','É',777}, {'Ò','Ê',1}, {'Ò','Ë',54}, {'Ò','Ì',29}, {'Ò','Í',39},
++{'Ò','Î',181}, {'Ò','Ï',1126}, {'Ò','Ð',38}, {'Ò','Ñ',159}, {'Ò','Ò',34}, {'Ò','Ó',76}, {'Ò','Ô',122}, {'Ò','Õ',355}, {'Ò','Ö',41}, {'Ò','×',98}, {'Ò','Ø',98}, {'Ò','Ù',169}, {'Ò','Ú',20}, {'Ò','Û',49}, {'Ò','Ü',7}, {'Ò','Ý',5},
++{'Ò','Þ',9}, {'Ò','á',3}, {'Ò','â',4}, {'Ò','ä',6}, {'Ò','å',2}, {'Ò','æ',2}, {'Ò','è',1}, {'Ò','ë',2}, {'Ò','í',1}, {'Ò','î',5}, {'Ò','ï',8}, {'Ò','ð',10}, {'Ò','ñ',6}, {'Ò','ó',2}, {'Ò','ô',4}, {'Ò','÷',3},
++{'Ò','ú',1}, {'Ò','ü',3}, {'Ò','þ',2}, {'Ó','À',27}, {'Ó','Á',306}, {'Ó','Â',33}, {'Ó','Ã',6}, {'Ó','Ä',41}, {'Ó','Å',973}, {'Ó','Æ',18}, {'Ó','Ç',18}, {'Ó','È',34}, {'Ó','É',295}, {'Ó','Ë',691}, {'Ó','Ì',519}, {'Ó','Í',222},
++{'Ó','Î',236}, {'Ó','Ï',403}, {'Ó','Ð',274}, {'Ó','Ñ',680}, {'Ó','Ò',58}, {'Ó','Ó',222}, {'Ó','Ô',1721}, {'Ó','Õ',146}, {'Ó','Ö',8}, {'Ó','×',259}, {'Ó','Ø',401}, {'Ó','Ù',79}, {'Ó','Ú',11}, {'Ó','Û',20}, {'Ó','Ü',10}, {'Ó','Ý',2},
++{'Ó','Þ',48}, {'Ó','á',6}, {'Ó','ä',2}, {'Ó','ç',2}, {'Ó','é',4}, {'Ó','ë',2}, {'Ó','ì',3}, {'Ó','í',4}, {'Ó','î',5}, {'Ó','ï',3}, {'Ó','ð',3}, {'Ó','ñ',1}, {'Ó','ò',1}, {'Ó','ó',2}, {'Ó','ô',1}, {'Ó','ö',1},
++{'Ó','÷',4}, {'Ó','ú',1}, {'Ó','ü',3}, {'Ó','þ',1}, {'Ô','À',13}, {'Ô','Á',1225}, {'Ô','Â',56}, {'Ô','Ã',10}, {'Ô','Ä',67}, {'Ô','Å',864}, {'Ô','Æ',1}, {'Ô','Ç',25}, {'Ô','È',24}, {'Ô','É',758}, {'Ô','Ê',1}, {'Ô','Ë',180},
++{'Ô','Ì',52}, {'Ô','Í',65}, {'Ô','Î',329}, {'Ô','Ï',2625}, {'Ô','Ð',119}, {'Ô','Ñ',103}, {'Ô','Ò',616}, {'Ô','Ó',337}, {'Ô','Ô',117}, {'Ô','Õ',398}, {'Ô','Ö',17}, {'Ô','×',436}, {'Ô','Ø',1292}, {'Ô','Ù',398}, {'Ô','Ú',33}, {'Ô','Û',3},
++{'Ô','Ü',37}, {'Ô','Ý',4}, {'Ô','Þ',87}, {'Ô','à',1}, {'Ô','á',29}, {'Ô','â',5}, {'Ô','ä',11}, {'Ô','å',9}, {'Ô','æ',2}, {'Ô','ç',3}, {'Ô','è',3}, {'Ô','é',16}, {'Ô','ë',21}, {'Ô','í',20}, {'Ô','î',24}, {'Ô','ï',10},
++{'Ô','ð',23}, {'Ô','ñ',16}, {'Ô','ò',2}, {'Ô','ó',10}, {'Ô','ô',13}, {'Ô','õ',2}, {'Ô','ö',2}, {'Ô','÷',25}, {'Ô','ú',5}, {'Ô','û',1}, {'Ô','ü',3}, {'Ô','þ',11}, {'Õ','À',156}, {'Õ','Á',45}, {'Õ','Â',142}, {'Õ','Ä',445},
++{'Õ','Å',83}, {'Õ','Æ',5}, {'Õ','Ç',161}, {'Õ','È',97}, {'Õ','É',111}, {'Õ','Ê',40}, {'Õ','Ë',169}, {'Õ','Ì',251}, {'Õ','Í',322}, {'Õ','Î',195}, {'Õ','Ï',50}, {'Õ','Ð',258}, {'Õ','Ñ',31}, {'Õ','Ò',203}, {'Õ','Ó',380}, {'Õ','Ô',442},
++{'Õ','Õ',24}, {'Õ','Ö',269}, {'Õ','×',184}, {'Õ','Ú',75}, {'Õ','Û',302}, {'Õ','Ü',19}, {'Õ','Ý',37}, {'Õ','Þ',241}, {'Õ','á',22}, {'Õ','â',4}, {'Õ','ä',20}, {'Õ','å',3}, {'Õ','æ',2}, {'Õ','ç',4}, {'Õ','è',5}, {'Õ','é',25},
++{'Õ','ë',9}, {'Õ','ì',6}, {'Õ','í',12}, {'Õ','î',18}, {'Õ','ï',13}, {'Õ','ð',18}, {'Õ','ñ',17}, {'Õ','ò',1}, {'Õ','ó',18}, {'Õ','ô',12}, {'Õ','õ',2}, {'Õ','÷',19}, {'Õ','ú',2}, {'Õ','û',3}, {'Õ','ü',5}, {'Õ','þ',6},
++{'Ö','Á',187}, {'Ö','Â',4}, {'Ö','Ä',145}, {'Ö','Å',690}, {'Ö','Ç',1}, {'Ö','È',1}, {'Ö','É',214}, {'Ö','Ë',35}, {'Ö','Ì',1}, {'Ö','Í',8}, {'Ö','Î',96}, {'Ö','Ï',13}, {'Ö','Ð',14}, {'Ö','Ñ',7}, {'Ö','Ò',7}, {'Ö','Ó',4},
++{'Ö','Ô',8}, {'Ö','Õ',69}, {'Ö','Ö',4}, {'Ö','×',9}, {'Ö','Ø',6}, {'Ö','Ú',1}, {'Ö','Ü',2}, {'Ö','Þ',1}, {'Ö','é',2}, {'Ö','î',1}, {'Ö','ï',1}, {'Ö','ð',3}, {'Ö','÷',1}, {'×','À',3}, {'×','Á',989}, {'×','Â',26},
++{'×','Ã',11}, {'×','Ä',74}, {'×','Å',911}, {'×','Æ',1}, {'×','Ç',43}, {'×','È',9}, {'×','É',387}, {'×','Ê',1}, {'×','Ë',105}, {'×','Ì',122}, {'×','Í',65}, {'×','Î',231}, {'×','Ï',1220}, {'×','Ð',118}, {'×','Ñ',48}, {'×','Ò',97},
++{'×','Ó',851}, {'×','Ô',123}, {'×','Õ',116}, {'×','Ö',26}, {'×','×',62}, {'×','Ø',33}, {'×','Ù',518}, {'×','Ú',88}, {'×','Û',53}, {'×','Ü',44}, {'×','Ý',2}, {'×','Þ',47}, {'×','á',11}, {'×','â',1}, {'×','ã',1}, {'×','ä',5},
++{'×','å',5}, {'×','ç',7}, {'×','è',8}, {'×','é',6}, {'×','ë',14}, {'×','ì',2}, {'×','í',9}, {'×','î',13}, {'×','ï',17}, {'×','ð',51}, {'×','ñ',4}, {'×','ò',7}, {'×','ó',18}, {'×','ô',4}, {'×','õ',2}, {'×','÷',6},
++{'×','û',7}, {'×','ü',2}, {'×','þ',2}, {'Ø','À',88}, {'Ø','Á',38}, {'Ø','Â',63}, {'Ø','Ã',15}, {'Ø','Ä',110}, {'Ø','Å',154}, {'Ø','Æ',5}, {'Ø','Ç',35}, {'Ø','È',20}, {'Ø','É',194}, {'Ø','Ê',1}, {'Ø','Ë',324}, {'Ø','Ì',36},
++{'Ø','Í',124}, {'Ø','Î',321}, {'Ø','Ï',92}, {'Ø','Ð',164}, {'Ø','Ñ',110}, {'Ø','Ò',41}, {'Ø','Ó',302}, {'Ø','Ô',152}, {'Ø','Õ',48}, {'Ø','Ö',11}, {'Ø','×',196}, {'Ø','Ú',58}, {'Ø','Û',85}, {'Ø','Ü',27}, {'Ø','Ý',1}, {'Ø','Þ',88},
++{'Ø','á',44}, {'Ø','â',8}, {'Ø','ä',18}, {'Ø','å',12}, {'Ø','æ',1}, {'Ø','ç',7}, {'Ø','è',3}, {'Ø','é',29}, {'Ø','ë',19}, {'Ø','í',18}, {'Ø','î',36}, {'Ø','ï',25}, {'Ø','ð',42}, {'Ø','ñ',21}, {'Ø','ò',4}, {'Ø','ó',25},
++{'Ø','ô',24}, {'Ø','õ',10}, {'Ø','ö',1}, {'Ø','÷',45}, {'Ø','ú',4}, {'Ø','û',1}, {'Ø','ü',4}, {'Ø','þ',14}, {'Ù','Á',10}, {'Ù','Â',66}, {'Ù','Ã',3}, {'Ù','Ä',65}, {'Ù','Å',138}, {'Ù','Æ',1}, {'Ù','Ç',37}, {'Ù','È',146},
++{'Ù','É',74}, {'Ù','Ê',264}, {'Ù','Ë',83}, {'Ù','Ì',305}, {'Ù','Í',164}, {'Ù','Î',141}, {'Ù','Ï',34}, {'Ù','Ð',231}, {'Ù','Ñ',10}, {'Ù','Ò',61}, {'Ù','Ó',189}, {'Ù','Ô',148}, {'Ù','Õ',35}, {'Ù','Ö',21}, {'Ù','×',248}, {'Ù','Ú',45},
++{'Ù','Û',80}, {'Ù','Ü',9}, {'Ù','Ý',1}, {'Ù','Þ',63}, {'Ù','à',1}, {'Ù','á',14}, {'Ù','â',6}, {'Ù','ä',8}, {'Ù','å',6}, {'Ù','æ',1}, {'Ù','ç',4}, {'Ù','é',12}, {'Ù','ë',10}, {'Ù','ì',2}, {'Ù','í',15}, {'Ù','î',16},
++{'Ù','ï',13}, {'Ù','ð',6}, {'Ù','ñ',10}, {'Ù','ò',3}, {'Ù','ó',7}, {'Ù','ô',10}, {'Ù','õ',1}, {'Ù','ö',1}, {'Ù','÷',28}, {'Ù','ú',4}, {'Ù','ü',5}, {'Ù','þ',4}, {'Ú','Á',889}, {'Ú','Â',39}, {'Ú','Ä',174}, {'Ú','Å',56},
++{'Ú','Ç',67}, {'Ú','È',3}, {'Ú','É',113}, {'Ú','Ë',16}, {'Ú','Ì',33}, {'Ú','Í',64}, {'Ú','Î',302}, {'Ú','Ï',109}, {'Ú','Ð',31}, {'Ú','Ñ',43}, {'Ú','Ò',53}, {'Ú','Ó',21}, {'Ú','Ô',7}, {'Ú','Õ',109}, {'Ú','Ö',18}, {'Ú','×',147},
++{'Ú','Ø',20}, {'Ú','Ù',71}, {'Ú','Ú',11}, {'Ú','Û',4}, {'Ú','Ü',4}, {'Ú','Þ',11}, {'Ú','á',1}, {'Ú','ã',1}, {'Ú','é',2}, {'Ú','ë',1}, {'Ú','í',4}, {'Ú','î',3}, {'Ú','ï',1}, {'Ú','ð',8}, {'Ú','ñ',2}, {'Ú','ó',4},
++{'Ú','ô',2}, {'Ú','ö',1}, {'Ú','÷',1}, {'Ú','þ',2}, {'Û','Á',150}, {'Û','Ã',1}, {'Û','Å',388}, {'Û','È',1}, {'Û','É',188}, {'Û','Ë',201}, {'Û','Ì',68}, {'Û','Í',4}, {'Û','Î',78}, {'Û','Ï',53}, {'Û','Ð',8}, {'Û','Ò',3},
++{'Û','Ó',2}, {'Û','Ô',17}, {'Û','Õ',45}, {'Û','×',5}, {'Û','Ø',200}, {'Û','Ü',1}, {'Û','ñ',1}, {'Ü','Â',1}, {'Ü','Ä',2}, {'Ü','Æ',2}, {'Ü','Ç',1}, {'Ü','È',1}, {'Ü','Ë',6}, {'Ü','Ì',9}, {'Ü','Í',6}, {'Ü','Î',3},
++{'Ü','Ò',8}, {'Ü','Ó',4}, {'Ü','Ô',531}, {'Ü','Ú',1}, {'Ü','Ü',2}, {'Ü','é',1}, {'Ý','Á',68}, {'Ý','Å',242}, {'Ý','É',96}, {'Ý','Î',7}, {'Ý','Ï',1}, {'Ý','Ò',1}, {'Ý','Ó',3}, {'Ý','Õ',20}, {'Ý','Ø',2}, {'Ý','ð',1},
++{'Ý','ô',1}, {'Þ','Á',294}, {'Þ','Â',1}, {'Þ','Å',874}, {'Þ','É',319}, {'Þ','Ë',76}, {'Þ','Ì',9}, {'Þ','Í',4}, {'Þ','Î',158}, {'Þ','Ï',10}, {'Þ','Ð',3}, {'Þ','Ñ',1}, {'Þ','Ò',8}, {'Þ','Ó',2}, {'Þ','Ô',536}, {'Þ','Õ',101},
++{'Þ','×',6}, {'Þ','Ø',49}, {'Þ','Û',37}, {'Þ','Þ',7}, {'Þ','å',1}, {'Þ','ç',1}, {'Þ','é',1}, {'Þ','î',3}, {'Þ','ï',1}, {'Þ','ó',3}, {'Þ','ô',2}, {'Þ','÷',1}, {'Þ','þ',1}, {'à','Ò',1}, {'à','Ú',2}, {'á','Á',1},
++{'á','Â',6}, {'á','Ä',7}, {'á','Å',23}, {'á','Æ',1}, {'á','Ç',5}, {'á','È',6}, {'á','É',4}, {'á','Ê',1}, {'á','Ë',27}, {'á','Ì',15}, {'á','Í',10}, {'á','Î',25}, {'á','Ï',21}, {'á','Ð',51}, {'á','Ñ',20}, {'á','Ò',12},
++{'á','Ó',13}, {'á','Ô',31}, {'á','Õ',15}, {'á','Ö',2}, {'á','×',60}, {'á','Ú',6}, {'á','Ü',8}, {'á','Þ',17}, {'á','á',1}, {'á','æ',1}, {'á','è',1}, {'á','é',1}, {'á','ë',1}, {'á','ì',1}, {'á','í',8}, {'á','î',2},
++{'á','ð',2}, {'á','ò',1}, {'á','ó',4}, {'á','ô',2}, {'á','÷',1}, {'á','ú',1}, {'á','û',1}, {'á','ü',1}, {'á','þ',1}, {'â','Á',3}, {'â','Å',16}, {'â','É',1}, {'â','Ì',4}, {'â','Î',3}, {'â','Ï',25}, {'â','Ò',10},
++{'â','Õ',9}, {'â','×',2}, {'â','Ù',8}, {'â','ä',1}, {'â','î',2}, {'â','ô',1}, {'â','÷',1}, {'ã','Á',7}, {'ã','É',3}, {'ã','Ó',1}, {'ã','Ô',1}, {'ã','Ù',1}, {'ä','Á',167}, {'ä','Å',17}, {'ä','É',3}, {'ä','Ì',7},
++{'ä','Í',3}, {'ä','Î',1}, {'ä','Ï',20}, {'ä','Ò',9}, {'ä','Õ',19}, {'ä','×',6}, {'å','Ä',1}, {'å','Å',1}, {'å','Æ',1}, {'å','È',1}, {'å','Ê',1}, {'å','Ì',5}, {'å','Í',2}, {'å','Ò',19}, {'å','Ó',69}, {'å','×',9},
++{'å','Ý',5}, {'å','ç',2}, {'å','è',1}, {'å','û',1}, {'æ','Á',7}, {'æ','Å',2}, {'æ','Æ',2}, {'æ','Ï',1}, {'æ','Ò',16}, {'æ','á',1}, {'ç','Á',6}, {'ç','Ä',16}, {'ç','Å',26}, {'ç','É',4}, {'ç','Ì',3}, {'ç','Í',1},
++{'ç','Ï',31}, {'ç','Ò',7}, {'ç','Õ',2}, {'ç','ï',2}, {'è','Á',8}, {'è','Å',3}, {'è','Ï',20}, {'è','Ò',14}, {'è','×',1}, {'è','æ',1}, {'é','Á',2}, {'é','Â',8}, {'é','Ä',20}, {'é','Å',7}, {'é','Æ',2}, {'é','Ç',7},
++{'é','È',3}, {'é','É',3}, {'é','Ë',27}, {'é','Ì',15}, {'é','Í',11}, {'é','Î',21}, {'é','Ï',26}, {'é','Ð',21}, {'é','Ñ',12}, {'é','Ò',4}, {'é','Ó',21}, {'é','Ô',37}, {'é','Õ',5}, {'é','Ö',1}, {'é','×',78}, {'é','Ú',17},
++{'é','Û',1}, {'é','Ü',2}, {'é','Þ',8}, {'é','á',1}, {'é','ì',2}, {'é','í',2}, {'é','ó',2}, {'é','÷',1}, {'ë','Á',100}, {'ë','É',4}, {'ë','Ë',1}, {'ë','Ì',14}, {'ë','Î',4}, {'ë','Ï',62}, {'ë','Ð',2}, {'ë','Ò',24},
++{'ë','Ô',10}, {'ë','Õ',62}, {'ë','ð',3}, {'ì','À',3}, {'ì','Á',7}, {'ì','Å',11}, {'ì','É',4}, {'ì','Ï',12}, {'ì','Ñ',1}, {'ì','Õ',15}, {'ì','á',1}, {'ì','é',1}, {'ì','ï',2}, {'í','Á',31}, {'í','Å',10}, {'í','Ç',1},
++{'í','É',44}, {'í','Ì',1}, {'í','Î',19}, {'í','Ï',93}, {'í','Ò',1}, {'í','Õ',6}, {'í','×',3}, {'í','Ù',29}, {'í','á',1}, {'í','é',1}, {'í','ï',1}, {'í','ð',1}, {'î','Á',56}, {'î','Å',111}, {'î','É',47}, {'î','Î',2},
++{'î','Ï',98}, {'î','Õ',100}, {'î','á',2}, {'î','é',3}, {'î','ú',1}, {'ï','Â',18}, {'ï','Ä',20}, {'ï','Ç',4}, {'ï','É',2}, {'ï','Ë',7}, {'ï','Ì',9}, {'ï','Í',2}, {'ï','Î',96}, {'ï','Ï',5}, {'ï','Ð',17}, {'ï','Ò',16},
++{'ï','Ó',21}, {'ï','Ô',35}, {'ï','×',1}, {'ï','Ú',2}, {'ï','Ü',6}, {'ï','Ý',1}, {'ï','Þ',13}, {'ï','å',2}, {'ï','î',6}, {'ï','ñ',1}, {'ï','ò',1}, {'ï','ô',1}, {'ï','õ',1}, {'ï','÷',1}, {'ð','Á',18}, {'ð','Å',148},
++{'ð','É',11}, {'ð','Ì',9}, {'ð','Ï',186}, {'ð','Ò',43}, {'ð','Õ',38}, {'ð','Ø',4}, {'ð','ó',3}, {'ñ','Â',4}, {'ñ','Ä',8}, {'ñ','Å',9}, {'ñ','Ç',5}, {'ñ','È',3}, {'ñ','É',4}, {'ñ','Ë',10}, {'ñ','Ì',5}, {'ñ','Í',6},
++{'ñ','Î',28}, {'ñ','Ï',24}, {'ñ','Ð',39}, {'ñ','Ò',4}, {'ñ','Ó',30}, {'ñ','Ô',7}, {'ñ','Õ',5}, {'ñ','Ö',2}, {'ñ','×',49}, {'ñ','Ú',12}, {'ñ','Û',3}, {'ñ','Ü',3}, {'ñ','Þ',4}, {'ñ','î',1}, {'ò','Á',22}, {'ò','Å',13},
++{'ò','É',5}, {'ò','Ï',13}, {'ò','Ò',1}, {'ò','Õ',1}, {'ò','ì',1}, {'ó','Á',19}, {'ó','Â',1}, {'ó','Ä',1}, {'ó','Å',56}, {'ó','È',2}, {'ó','É',16}, {'ó','Ë',22}, {'ó','Ì',11}, {'ó','Í',12}, {'ó','Î',6}, {'ó','Ï',26},
++{'ó','Ð',6}, {'ó','Ñ',1}, {'ó','Ò',6}, {'ó','Ó',3}, {'ó','Ô',35}, {'ó','Õ',4}, {'ó','×',4}, {'ó','Ù',1}, {'ó','Ü',1}, {'ó','Þ',2}, {'ó','á',2}, {'ó','ó',3}, {'ó','÷',1}, {'ô','Á',79}, {'ô','Å',18}, {'ô','É',17},
++{'ô','Ï',34}, {'ô','Ñ',3}, {'ô','Ò',11}, {'ô','Õ',31}, {'ô','×',1}, {'ô','Ø',2}, {'ô','Ù',55}, {'ô','á',4}, {'ô','å',1}, {'ô','é',1}, {'ô','ï',1}, {'õ','Â',2}, {'õ','Ä',3}, {'õ','Å',2}, {'õ','Ç',1}, {'õ','È',2},
++{'õ','É',1}, {'õ','Ê',2}, {'õ','Ë',1}, {'õ','Ì',2}, {'õ','Í',9}, {'õ','Î',5}, {'õ','Ï',1}, {'õ','Ð',1}, {'õ','Ó',9}, {'õ','Ô',8}, {'õ','Ö',4}, {'õ','×',3}, {'õ','Ú',4}, {'õ','Û',1}, {'õ','Þ',1}, {'ö','Á',5},
++{'ö','Ä',1}, {'ö','Å',14}, {'ö','É',5}, {'ö','Ï',2}, {'ö','Ò',1}, {'÷','Á',15}, {'÷','Ä',5}, {'÷','Å',98}, {'÷','Ç',3}, {'÷','É',7}, {'÷','Ë',5}, {'÷','Ì',8}, {'÷','Í',7}, {'÷','Î',6}, {'÷','Ï',107}, {'÷','Ð',18},
++{'÷','Ò',2}, {'÷','Ó',74}, {'÷','Ô',6}, {'÷','Õ',1}, {'÷','×',2}, {'÷','Ù',39}, {'÷','Ú',5}, {'÷','Ü',8}, {'÷','Þ',3}, {'÷','á',1}, {'÷','è',1}, {'÷','ë',2}, {'÷','í',1}, {'÷','ï',3}, {'÷','ð',8}, {'÷','ó',2},
++{'÷','û',2}, {'ú','Á',31}, {'ú','Å',1}, {'ú','É',2}, {'ú','Î',23}, {'ú','Ò',2}, {'ú','Õ',12}, {'ú','×',3}, {'ú','é',1}, {'û','Á',3}, {'û','Å',7}, {'û','É',8}, {'û','Ô',8}, {'û','Õ',7}, {'û','Û',1}, {'û','é',1},
++{'ü','Â',5}, {'ü','È',1}, {'ü','Ê',1}, {'ü','Ì',4}, {'ü','Î',1}, {'ü','Ò',10}, {'ü','Ó',1}, {'ü','Ô',61}, {'ü','×',1}, {'ü','ô',1}, {'þ','Å',46}, {'þ','Ô',60}, {'þ','Õ',8}, {'þ','Ø',1}
++};
++
++static int
++is_win_charset(char *txt, int len){
++ int j,ki=0,wi=0,d=839,l,koi_stat=0,win_stat=0,ws,ks;
++ unsigned char a,b;
++ l=strlen(txt);
++ if ((len)&&(l>len)) l=len;
++ for(j=0;j<l-1;j++){
++ a=txt[j];
++ b=txt[j+1];
++ //skip bottom half of table
++ if(a<128 || b<128)
++ continue;
++ wi=839;
++ ki=839;
++ ws=0;
++ ks=0;
++ d=839;
++ //binary search through lng_stats
++ do{
++ d>>=1;
++ if(!ws){
++ if(a==win[wi].a){
++ if(b==win[wi].b){
++ ws=1;
++ win_stat+=win[wi].rate;
++ }else if(b<win[wi].b){
++ wi-=d;
++ }else{ //b>win[wi].b
++ wi+=d;
++ }
++ }else if(a<win[wi].a){
++ wi-=d;
++ }else{ //a>win[wi].a
++ wi+=d;
++ }
++ }
++ if(!ks){
++ if(a==koi[ki].a){
++ if(b==koi[ki].b){
++ ks=1;
++ koi_stat+=koi[ki].rate;
++ }else if(b<koi[ki].b){
++ ki-=d;
++ }else{ //b>win[wi].b
++ ki+=d;
++ }
++ }else if(a<koi[ki].a){
++ ki-=d;
++ }else{ //a>win[wi].a
++ ki+=d;
++ }
++ }
++ }while(d);
++ }
++// fprintf(stderr,"\nwin %i, koi %i\n",win_stat,koi_stat);
++ if(win_stat>koi_stat)
++ return 1;
++ else
++ return 0;
++}
++
++/* Only CP1251 and KOI8-R still */
++int autocharset_russian(char *buf,int len) {
++ if (is_win_charset(buf,len)) return 0;
++ return 1;
++}
+diff -Naur xmms-1.2.8/libxmms/charset.c xmms-1.2.8-new/libxmms/charset.c
+--- xmms-1.2.8/libxmms/charset.c 1970-01-01 01:00:00.000000000 +0100
++++ xmms-1.2.8-new/libxmms/charset.c 2003-09-06 19:42:29.000000000 +0200
+@@ -0,0 +1,512 @@
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <errno.h>
++#include <glib.h>
++#include <iconv.h>
++#include <assert.h>
++
++#include "charset.h"
++#include "charset_config.h"
++
++#define LOCALE_STRING_LENGTH 64
++static char charset_default[LOCALE_STRING_LENGTH];
++static char xmms_charset_tmp[CHARSET_MAX_STRING_SIZE];
++
++static int xmms_charset_changed = -1;
++
++static int xmms_charset_id3;
++static int xmms_charset_output;
++static int xmms_charset_fs;
++static int xmms_charset_pl;
++
++static iconv_t iconv_id3=(iconv_t)-1; /* id3 to output */
++static iconv_t iconv_output=(iconv_t)-1; /* output to id3 */
++static iconv_t iconv_fs=(iconv_t)-1; /* fs to output */
++static iconv_t iconv_utf=(iconv_t)-1; /* id3 to UTF-8 */
++static iconv_t iconv_pl2fs=(iconv_t)-1; /* playlist to fs */
++static iconv_t iconv_fs2pl=(iconv_t)-1; /* fs to playlist */
++
++static int xmms_autocharset_mode = 0;
++static int xmms_autocharset_changed = 0;
++static iconv_t *xmms_autocharset = NULL;
++static iconv_t *xmms_autocharset_id2id = NULL;
++static iconv_t *xmms_autocharset_id2fs = NULL;
++static int xmms_autocharset_number = -1;
++
++static int recode_fsout;
++static int recode_fs2utf;
++#include "charset_auto_russian.h"
++
++extern int errno;
++
++/*****************************************************************************
++************************ Auto Recoding ***************************************
++*****************************************************************************/
++static int xmms_autocharset_number_charsets(int m) {
++ const char **encodings;
++ int i;
++
++ if (!m) m=xmms_autocharset_mode;
++ encodings=autocharset_list[m].encodings;
++ for (i=0;encodings[i];i++);
++ return i;
++}
++
++
++static void xmms_autocharset_iconv_close() {
++ int i;
++
++ for (i=0;i<xmms_autocharset_number;i++) {
++ if ((xmms_autocharset)&&(xmms_autocharset[i] != (iconv_t)-1)&&(xmms_autocharset[i] != (iconv_t)-2))
++ iconv_close(xmms_autocharset[i]);
++ if ((xmms_autocharset_id2id)&&(xmms_autocharset_id2id[i] != (iconv_t)-1)&&(xmms_autocharset_id2id[i] != (iconv_t)-2))
++ iconv_close(xmms_autocharset_id2id[i]);
++ if ((xmms_autocharset_id2fs)&&(xmms_autocharset_id2fs[i] != (iconv_t)-1)&&(xmms_autocharset_id2fs[i] != (iconv_t)-2))
++ iconv_close(xmms_autocharset_id2fs[i]);
++ }
++ if (xmms_autocharset) free(xmms_autocharset);
++ if (xmms_autocharset_id2id) free(xmms_autocharset_id2id);
++ if (xmms_autocharset_id2fs) free(xmms_autocharset_id2fs);
++
++ xmms_autocharset=NULL;
++ xmms_autocharset_id2id=NULL;
++ xmms_autocharset_id2fs=NULL;
++ xmms_autocharset_number = -1;
++}
++
++void xmms_autocharset_new_mode(int m) {
++ xmms_autocharset_mode=m;
++ xmms_autocharset_changed=1;
++}
++
++void xmms_autocharset_init(int m) {
++ xmms_autocharset_mode=m;
++ xmms_autocharset_changed=1;
++}
++
++void xmms_autocharset_free() {
++ xmms_autocharset_iconv_close();
++}
++
++static void xmms_autocharset_iconv_open() {
++ const char **encodings;
++ const char *output, *id3, *fs;
++ int i;
++
++ if ((!xmms_autocharset_changed)&&(!xmms_charset_changed)) return;
++ xmms_autocharset_iconv_close();
++ xmms_autocharset_changed=0;
++
++ xmms_autocharset_number = xmms_autocharset_number_charsets(0);
++ if (!xmms_autocharset_number) return;
++
++ xmms_autocharset = (iconv_t*)malloc((xmms_autocharset_number+1)*sizeof(iconv_t));
++ xmms_autocharset_id2id = (iconv_t*)malloc((xmms_autocharset_number+1)*sizeof(iconv_t));
++ xmms_autocharset_id2fs = (iconv_t*)malloc((xmms_autocharset_number+1)*sizeof(iconv_t));
++ if ((!xmms_autocharset)||(!xmms_autocharset_id2id)||(!xmms_autocharset_id2fs)) {
++ if (xmms_autocharset) {
++ free(xmms_autocharset);
++ xmms_autocharset=NULL;
++ }
++ if (xmms_autocharset_id2id) {
++ free(xmms_autocharset_id2id);
++ xmms_autocharset_id2id=NULL;
++ }
++ if (xmms_autocharset_id2fs) {
++ free(xmms_autocharset_id2fs);
++ xmms_autocharset_id2fs=NULL;
++ }
++ xmms_autocharset_number = -1;
++ perror("iconv: allocate memory");
++ return;
++ }
++
++
++ if (xmms_charset_output) output=charset_list[xmms_charset_output];
++ else output=charset_default;
++ if (xmms_charset_id3) id3=charset_list[xmms_charset_id3];
++ else id3=charset_default;
++ if (xmms_charset_fs) fs=charset_list[xmms_charset_fs];
++ else fs=output;
++
++ encodings = autocharset_list[xmms_autocharset_mode].encodings;
++
++ for (i=0;encodings[i];i++) {
++ if (strcmp(output,encodings[i]))
++ xmms_autocharset[i] = iconv_open(output,encodings[i]);
++ else
++ xmms_autocharset[i] = (iconv_t)-2;
++
++ if (strcmp(id3,encodings[i]))
++ xmms_autocharset_id2id[i] = iconv_open(id3,encodings[i]);
++ else
++ xmms_autocharset_id2id[i] = (iconv_t)-2;
++
++ if (strcmp(fs,encodings[i]))
++ xmms_autocharset_id2fs[i] = iconv_open(fs,encodings[i]);
++ else
++ xmms_autocharset_id2fs[i] = (iconv_t)-2;
++ }
++}
++
++
++static iconv_t xmms_autocharset_get(char *buf, int len) {
++ int i;
++ xmms_autocharset_iconv_open();
++ if ((xmms_autocharset)&&(xmms_autocharset_number>0)) {
++ i=autocharset_list[xmms_autocharset_mode].func(buf,len);
++ if (i<xmms_autocharset_number) return xmms_autocharset[i];
++ }
++ return (iconv_t)-1;
++}
++
++static iconv_t xmms_autocharset_get_id2id(char *buf, int len) {
++ int i;
++ xmms_autocharset_iconv_open();
++ if ((xmms_autocharset_id2id)&&(xmms_autocharset_number>0)) {
++ i=autocharset_list[xmms_autocharset_mode].func(buf,len);
++ if (i<xmms_autocharset_number) return xmms_autocharset_id2id[i];
++ }
++ return (iconv_t)-1;
++}
++
++static iconv_t xmms_autocharset_get_id2fs(char *buf, int len) {
++ int i;
++ xmms_autocharset_iconv_open();
++ if ((xmms_autocharset_id2fs)&&(xmms_autocharset_number>0)) {
++ i=autocharset_list[xmms_autocharset_mode].func(buf,len);
++ if (i<xmms_autocharset_number) return xmms_autocharset_id2fs[i];
++ }
++ return (iconv_t)-1;
++}
++
++/*****************************************************************************
++************************ Recoding ********************************************
++*****************************************************************************/
++
++static void xmms_charset_iconv_close() {
++ if (iconv_id3 != (iconv_t)-1) {
++ if (iconv_close(iconv_id3)<0) perror("iconv close: id3");
++ iconv_id3 = (iconv_t)-1;
++ }
++ if (iconv_output != (iconv_t)-1) {
++ if (iconv_close(iconv_output)<0) perror("iconv close: output");
++ iconv_output = (iconv_t)-1;
++ }
++ if (iconv_fs != (iconv_t)-1) {
++ if (iconv_close(iconv_fs)<0) perror("iconv close: fs");
++ iconv_fs = (iconv_t)-1;
++ }
++ if (iconv_fs2pl != (iconv_t)-1) {
++ if (iconv_close(iconv_fs2pl)<0) perror("iconv close: fs2pl");
++ iconv_fs2pl = (iconv_t)-1;
++ }
++ if (iconv_pl2fs != (iconv_t)-1) {
++ if (iconv_close(iconv_pl2fs)<0) perror("iconv close: pl2fs");
++ iconv_pl2fs = (iconv_t)-1;
++ }
++ if (iconv_utf != (iconv_t)-1) {
++ if (iconv_close(iconv_utf)<0) perror("iconv close: utf");
++ iconv_utf=(iconv_t)-1;
++ }
++}
++
++static void xmms_charset_iconv_open() {
++ const char *id3, *output, *fs, *pl;
++ assert(xmms_charset_changed>=0);
++ if (!xmms_charset_changed) return;
++
++ xmms_charset_iconv_close();
++ if (xmms_charset_id3) id3=charset_list[xmms_charset_id3];
++ else id3=charset_default;
++ if (xmms_charset_output) output=charset_list[xmms_charset_output];
++ else output=charset_default;
++ if (xmms_charset_fs) fs=charset_list[xmms_charset_fs];
++ else fs=output;
++ if (xmms_charset_pl) pl=charset_list[xmms_charset_pl];
++ else pl=id3;
++
++
++ if ((xmms_charset_id3 != xmms_charset_output)&&(strcmp(id3,output))) {
++ iconv_id3 = iconv_open(output, id3);
++ iconv_output = iconv_open(id3, output);
++ if (!xmms_charset_fs) iconv_fs = iconv_open(id3, fs);
++ }
++
++ if (strcmp(fs,id3)) {
++ iconv_fs = iconv_open(id3, fs);
++ }
++
++ if (strcmp(fs,pl)) {
++ iconv_fs2pl = iconv_open(pl, fs);
++ iconv_pl2fs = iconv_open(fs,pl);
++ }
++
++ if (strcmp(id3, "UTF-8")) {
++ iconv_utf = iconv_open("UTF-8",id3);
++ }
++
++ if ((xmms_charset_fs)&&(xmms_charset_fs!=xmms_charset_output)) {
++ recode_fsout=1;
++ } else {
++ recode_fsout=0;
++ }
++
++ if (strcmp(fs, "UTF-8")) {
++ recode_fs2utf=1;
++ } else {
++ recode_fs2utf=0;
++ }
++
++ xmms_charset_changed = 0;
++}
++
++
++static int xmms_charset_number(char *c) {
++ int i;
++ if (!c) return 0;
++ for (i=0;charset_list[i];i++)
++ if (strcmp(charset_list[i], c)==0) return i;
++ return 0; /* Default, - if not found */
++}
++
++static void xmms_charset_setdefault() {
++ char *str1, *str2;
++ str1 = getenv(ENV_VARIABLE);
++ if (str1) {
++ str2 = strrchr(str1,'.');
++ if (str2) strncpy(charset_default,str2+1,LOCALE_STRING_LENGTH);
++ else strncpy(charset_default,str1,LOCALE_STRING_LENGTH);
++ charset_default[LOCALE_STRING_LENGTH-1]=0;
++ str2 = strchr(charset_default,'@');
++ if (str2) *str2 = 0;
++ } else {
++ strcpy(charset_default,"UTF-8");
++ }
++}
++
++void xmms_charset_init(char *id3, char *output, char *fs, char *pl) {
++ if (xmms_charset_changed<0) xmms_charset_setdefault();
++
++ xmms_charset_changed = 1;
++ xmms_charset_id3 = xmms_charset_number(id3);
++ xmms_charset_output = xmms_charset_number(output);
++ xmms_charset_fs = xmms_charset_number(fs);
++ xmms_charset_pl = xmms_charset_number(pl);
++}
++
++void xmms_charset_free() {
++ xmms_charset_iconv_close();
++}
++
++void xmms_charset_new_charsets(int id3, int output, int fs, int pl) {
++ if (xmms_charset_changed<0)
++ xmms_charset_setdefault();
++ else
++ if ((xmms_charset_id3==id3)&&(xmms_charset_output==output)&&(xmms_charset_fs==fs)&&(xmms_charset_pl==pl)) return;
++
++ xmms_charset_changed = 1;
++ xmms_charset_id3 = id3;
++ xmms_charset_output = output;
++ xmms_charset_fs = fs;
++ xmms_charset_pl = pl;
++}
++
++// Multi-byte strings still unsupported
++static void iconv_copysymbol(char **in_buf, int *in_left, char **out_buf, int *out_left) {
++ if ((out_left>0)&&(in_left>0)) {
++ (**out_buf)=(**in_buf);
++ (*out_buf)++;
++ (*in_buf)++;
++ (*in_left)--;
++ (*out_left)--;
++ }
++}
++
++static char *xmms_charset_recode(iconv_t icnv, char *buf, int len, int *rlen) {
++ char *in_buf, *out_buf, *res, err;
++ int in_left, out_left, olen;
++ int errors=0;
++
++ assert(buf);
++
++ if (!len) {
++ len=strlen(buf);
++ } else {
++ olen=strlen(buf);
++ if (olen<len) len=olen;
++ }
++
++ if (iconv(icnv, NULL, NULL, NULL, NULL) == -1) {
++ perror( "iconv convert: initialize" );
++ return NULL;
++ }
++
++ in_buf = buf;
++ in_left = len;
++ out_buf = &xmms_charset_tmp;
++ out_left = CHARSET_MAX_STRING_SIZE;
++
++loop:
++ err=iconv(icnv, &in_buf, &in_left, &out_buf, &out_left);
++// printf("%s, %i %u\n",buf,err,errno);
++ if (err<0) {
++ if (errno==E2BIG) {
++ *(int*)(xmms_charset_tmp+(CHARSET_MAX_STRING_SIZE-sizeof(int)))=0;
++ } else if (errno==EILSEQ) {
++ if (errors<CHARSET_MAX_ERRORS) {
++ errors++;
++ iconv_copysymbol(&in_buf, &in_left, &out_buf, &out_left);
++ if (in_left>0) goto loop;
++ } else {
++ perror("iconv convert: invalid encoding?");
++ return NULL;
++ }
++ } else {
++ perror("iconv convert: convert");
++// perror(buf);
++ return NULL;
++ }
++ }
++
++ olen = CHARSET_MAX_STRING_SIZE - out_left;
++ res = g_malloc(olen+1);
++ if (!res) {
++ perror( "iconv convert: malloc" );
++ return NULL;
++ }
++
++ memcpy(res,xmms_charset_tmp,olen);
++ res[olen]=0;
++ if (rlen) *rlen=olen;
++ return res;
++}
++
++char *xmms_charset_recode_id3(char *buf, int len, int *rlen) {
++ iconv_t icnv;
++
++ xmms_charset_iconv_open();
++ icnv = xmms_autocharset_get(buf,len);
++
++ if (icnv == (iconv_t)-2) return NULL;
++
++ if (icnv != (iconv_t)-1)
++ return xmms_charset_recode(icnv, buf, len, rlen);
++
++ if (iconv_id3 != (iconv_t)-1)
++ return xmms_charset_recode(iconv_id3, buf, len, rlen);
++ return NULL;
++}
++
++char *xmms_charset_recode_output(char *buf, int len, int *rlen) {
++ xmms_charset_iconv_open();
++ if (iconv_output != (iconv_t)-1)
++ return xmms_charset_recode(iconv_output, buf, len, rlen);
++ return NULL;
++}
++
++char *xmms_charset_recode_fs(char *buf, int len, int *rlen) {
++ xmms_charset_iconv_open();
++ if (iconv_fs != (iconv_t)-1)
++ return xmms_charset_recode(iconv_fs, buf, len, rlen);
++ return NULL;
++}
++
++char *xmms_charset_recode_fsout(char *buf, int len, int *rlen) {
++ int olen;
++ char *str = NULL;
++
++ xmms_charset_iconv_open();
++ if (recode_fsout) {
++ if (iconv_fs != (iconv_t)-1) {
++ str = xmms_charset_recode(iconv_fs, buf, len, &olen);
++ if (!str) return NULL;
++ len=olen;
++ }
++ if (iconv_id3 != (iconv_t)-1) {
++ buf = xmms_charset_recode(iconv_id3, str ? str : buf, len, &olen);
++ if (str) free(str);
++ } else if (str) {
++ buf = str;
++ } else {
++ buf = NULL;
++ }
++ if (rlen) *rlen=olen;
++ return buf;
++ }
++ return NULL;
++}
++
++char *xmms_charset_recode_fs2pl(char *buf, int len, int *rlen) {
++ xmms_charset_iconv_open();
++ if (iconv_fs2pl != (iconv_t)-1)
++ return xmms_charset_recode(iconv_fs2pl, buf, len, rlen);
++ return NULL;
++}
++
++char *xmms_charset_recode_pl2fs(char *buf, int len, int *rlen) {
++ iconv_t icnv;
++
++ xmms_charset_iconv_open();
++ icnv = xmms_autocharset_get_id2fs(buf,len);
++
++ if (icnv==(iconv_t)-2) return NULL;
++
++ if (icnv != (iconv_t)-1)
++ return xmms_charset_recode(icnv, buf, len, rlen);
++
++ if (iconv_pl2fs != (iconv_t)-1)
++ return xmms_charset_recode(iconv_pl2fs, buf, len, rlen);
++ return NULL;
++}
++
++
++char *xmms_charset_recode_id2utf(char *buf, int len, int *rlen) {
++ char *str = NULL;
++ int olen;
++ iconv_t icnv;
++
++ xmms_charset_iconv_open();
++
++ icnv = xmms_autocharset_get_id2id(buf,len);
++ if ((icnv != (iconv_t)-1)&&(icnv != (iconv_t)-2)) {
++ str = xmms_charset_recode(icnv, buf, len, &olen);
++ if (!str) return NULL;
++ len = olen;
++ }
++
++ xmms_charset_iconv_open();
++ if (iconv_utf != (iconv_t)-1) {
++ buf=xmms_charset_recode(iconv_utf, str ? str : buf, len, rlen);
++ if (str) free(str);
++ return buf;
++ }
++
++ return NULL;
++}
++
++char *xmms_charset_recode_fs2utf(char *buf, int len, int *rlen) {
++ int olen;
++ char *str = NULL;
++
++ xmms_charset_iconv_open();
++ if (recode_fs2utf) {
++ if (iconv_fs != (iconv_t)-1) {
++ str = xmms_charset_recode(iconv_fs, buf, len, &olen);
++ if (!str) return NULL;
++ len=olen;
++ }
++ if (iconv_utf != (iconv_t)-1) {
++ buf = xmms_charset_recode(iconv_utf, str ? str : buf, len, &olen);
++ if (str) free(str);
++ } else if (str) {
++ buf = str;
++ } else {
++ buf = NULL;
++ }
++ if (rlen) *rlen=olen;
++ return buf;
++ }
++ return NULL;
++}
+diff -Naur xmms-1.2.8/libxmms/charset_config.h xmms-1.2.8-new/libxmms/charset_config.h
+--- xmms-1.2.8/libxmms/charset_config.h 1970-01-01 01:00:00.000000000 +0100
++++ xmms-1.2.8-new/libxmms/charset_config.h 2003-09-06 19:42:29.000000000 +0200
+@@ -0,0 +1,13 @@
++/* Misc configuration options */
++
++#define ENV_VARIABLE "LANG"
++#define CHARSET_MAX_STRING_SIZE 1024 /* 256 symbols in UTF-32 */
++#define CHARSET_MAX_ERRORS 3
++
++const char *charset_list[] = { "Default", "KOI8-R", "CP1251", "CP866", "UTF-8", NULL };
++
++const int autocharset_list_ni=2;
++const struct autocharset_list_t autocharset_list[] = {
++ {"Off", NULL, {NULL}},
++ {"Russian", &autocharset_russian, {"CP1251","KOI8-R"} },
++};
+diff -Naur xmms-1.2.8/libxmms/charset.h xmms-1.2.8-new/libxmms/charset.h
+--- xmms-1.2.8/libxmms/charset.h 1970-01-01 01:00:00.000000000 +0100
++++ xmms-1.2.8-new/libxmms/charset.h 2003-09-06 19:42:29.000000000 +0200
+@@ -0,0 +1,36 @@
++#ifndef XMMS_CHARSET_H
++#define XMMS_CHARSET_H
++
++#define AUTOCHARSET_MAX_ENCODINGS 5
++int autocharset_russian(char *buf,int len);
++
++struct autocharset_list_t {
++ char *title;
++ int (*func)(char *buf, int len);
++ char *encodings[AUTOCHARSET_MAX_ENCODINGS];
++};
++
++extern const char *charset_list[];
++extern const int autocharset_list_ni;
++extern const struct autocharset_list_t autocharset_list[];
++
++/* Initialization functions */
++void xmms_autocharset_new_mode(int m);
++void xmms_autocharset_init(int m);
++void xmms_autocharset_free();
++
++void xmms_charset_init(char *id3, char *output, char *fs, char *pl);
++void xmms_charset_free();
++void xmms_charset_new_charsets(int id3, int output, int fs, int pl);
++
++/* Recoding functions */
++char *xmms_charset_recode_id3(char *buf, int len, int *rlen);
++char *xmms_charset_recode_output(char *buf, int len, int *rlen);
++char *xmms_charset_recode_fs(char *buf, int len, int *rlen);
++char *xmms_charset_recode_fsout(char *buf, int len, int *rlen);
++char *xmms_charset_recode_id2utf(char *buf, int len, int *rlen);
++char *xmms_charset_recode_fs2utf(char *buf, int len, int *rlen);
++char *xmms_charset_recode_fs2pl(char *buf, int len, int *rlen);
++char *xmms_charset_recode_pl2fs(char *buf, int len, int *rlen);
++
++#endif /* XMMS_CHARSET_H */
+diff -Naur xmms-1.2.8/libxmms/Makefile.am xmms-1.2.8-new/libxmms/Makefile.am
+--- xmms-1.2.8/libxmms/Makefile.am 2003-09-04 23:00:18.000000000 +0200
++++ xmms-1.2.8-new/libxmms/Makefile.am 2003-09-06 20:11:31.000000000 +0200
+@@ -15,7 +15,9 @@
+ formatter.c formatter.h \
+ titlestring.c titlestring.h \
+ xentry.c xentry.h \
+-xconvert.c xconvert.h
++xconvert.c xconvert.h \
++charset.c charset.h \
++charset_auto_russian.h charset_config.h
+
+-xmmsinclude_HEADERS = configfile.h xmmsctrl.h dirbrowser.h util.h formatter.h titlestring.h
++xmmsinclude_HEADERS = configfile.h xmmsctrl.h dirbrowser.h util.h formatter.h titlestring.h charset.h charset_auto_russian.h
+
+diff -Naur xmms-1.2.8/libxmms/Makefile.in xmms-1.2.8-new/libxmms/Makefile.in
+--- xmms-1.2.8/libxmms/Makefile.in 2003-09-04 23:01:19.000000000 +0200
++++ xmms-1.2.8-new/libxmms/Makefile.in 2003-09-06 19:42:29.000000000 +0200
+@@ -101,10 +101,12 @@
+ formatter.c formatter.h \
+ titlestring.c titlestring.h \
+ xentry.c xentry.h \
+-xconvert.c xconvert.h
++xconvert.c xconvert.h \
++charset.c charset.h \
++charset_auto_russian.h charset_config.h
+
++xmmsinclude_HEADERS = configfile.h xmmsctrl.h dirbrowser.h util.h formatter.h titlestring.h charset.h charset_auto_russian.h
+
+-xmmsinclude_HEADERS = configfile.h xmmsctrl.h dirbrowser.h util.h formatter.h titlestring.h
+ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+ mkinstalldirs = $(SHELL) $(top_srcdir)/../mkinstalldirs
+ CONFIG_HEADER = config.h
+@@ -118,7 +120,7 @@
+ LIBS = @LIBS@
+ libxmms_la_DEPENDENCIES =
+ libxmms_la_OBJECTS = configfile.lo xmmsctrl.lo dirbrowser.lo util.lo \
+-formatter.lo titlestring.lo xentry.lo xconvert.lo
++formatter.lo titlestring.lo xentry.lo xconvert.lo charset.lo
+ CFLAGS = @CFLAGS@
+ COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+ LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+diff -Naur xmms-1.2.8/libxmms/titlestring.c xmms-1.2.8-new/libxmms/titlestring.c
+--- xmms-1.2.8/libxmms/titlestring.c 2002-01-08 15:09:08.000000000 +0100
++++ xmms-1.2.8-new/libxmms/titlestring.c 2003-09-06 19:42:29.000000000 +0200
+@@ -40,6 +40,8 @@
+ gchar *xmms_get_titlestring(gchar *fmt, TitleInput *input)
+ {
+ gchar c, *p, outbuf[256], convert[16], *string;
++ gchar *cstring = NULL;
++ gint stringlen;
+ gint numdigits, numpr, val, size, i;
+ gint f_left, f_space, f_zero, someflag, width, precision;
+ gboolean did_output = FALSE;
+@@ -164,9 +166,17 @@
+ goto Print_string;
+ case 'f':
+ string = VS(input, file_name);
++ if (string) {
++ cstring=xmms_charset_recode_fs(string,0,&stringlen);
++ if (cstring) string=cstring;
++ }
+ goto Print_string;
+ case 'F':
+ string = VS(input, file_path);
++ if (string) {
++ cstring=xmms_charset_recode_fs(string,0,&stringlen);
++ if (cstring) string=cstring;
++ }
+ goto Print_string;
+ case 'g':
+ string = VS(input, genre);
+@@ -208,6 +218,10 @@
+ while ((c = *string++) != '\0')
+ PUTCH(c);
+ }
++ if (cstring) {
++ g_free(cstring);
++ cstring=NULL;
++ }
+
+ RIGHTPAD(width - numpr);
+ break;
+diff -Naur xmms-1.2.8/xmms/charset.c xmms-1.2.8-new/xmms/charset.c
+--- xmms-1.2.8/xmms/charset.c 1970-01-01 01:00:00.000000000 +0100
++++ xmms-1.2.8-new/xmms/charset.c 2003-09-06 19:42:29.000000000 +0200
+@@ -0,0 +1,393 @@
++#include <stdio.h>
++#include <locale.h>
++#include <string.h>
++#include <gtk/gtk.h>
++#include "xmms.h"
++#include "libxmms/charset.h"
++#include "charset.h"
++
++#define CHARSET_MAX_STRING_SIZE 1024 /* 256 symbols in UTF-32 */
++static char xmms_charset_tmp[CHARSET_MAX_STRING_SIZE];
++
++extern TextBox *playlistwin_sinfo;
++
++static gint selected_id3_charset, selected_output_charset, selected_fs_charset;
++static gint selected_pl_charset;
++static gint selected_autocharset;
++static GtkWidget *prefswin_options_font_shade_entry, *prefswin_options_font_shade_browse;
++
++static void prefswin_font_shade_browse_ok(GtkWidget * w, gpointer data)
++{
++ GtkFontSelectionDialog *fontsel = GTK_FONT_SELECTION_DIALOG(data);
++ gchar *fontname;
++
++ fontname = gtk_font_selection_dialog_get_font_name(fontsel);
++
++ if (fontname)
++ gtk_entry_set_text(GTK_ENTRY(prefswin_options_font_shade_entry), fontname);
++
++ gtk_widget_destroy(GTK_WIDGET(fontsel));
++}
++
++static void prefswin_font_shade_browse_cb(GtkWidget * w, gpointer data)
++{
++ static GtkWidget *fontsel;
++
++ if (fontsel != NULL)
++ return;
++
++ fontsel = gtk_font_selection_dialog_new(_("Select playlist font:"));
++ gtk_font_selection_dialog_set_font_name(GTK_FONT_SELECTION_DIALOG(fontsel), gtk_entry_get_text(GTK_ENTRY(prefswin_options_font_shade_entry)));
++ gtk_signal_connect(GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel)->ok_button), "clicked", GTK_SIGNAL_FUNC(prefswin_font_shade_browse_ok), fontsel);
++ gtk_signal_connect_object(GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel)->cancel_button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(fontsel));
++ gtk_signal_connect(GTK_OBJECT(fontsel), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &fontsel);
++ gtk_widget_show(fontsel);
++}
++
++
++static void prefswin_charset_cb(GtkWidget * w, gpointer item)
++{
++ int i;
++ i = GPOINTER_TO_INT(item);
++ if (i>=0x3FFFF) selected_pl_charset=i-0x3FFFF;
++ else if (i>=0x2FFFF) selected_autocharset=i-0x2FFFF;
++ else if (i>=0x1FFFF) selected_fs_charset=i-0x1FFFF;
++ else if (i>=0xFFFF) selected_output_charset=i-0xFFFF;
++ else selected_id3_charset=i;
++}
++
++static void charset_options_add(Config *cfg, GtkOptionMenu *cmenu1, GtkOptionMenu *cmenu2, GtkOptionMenu *cmenu3, GtkOptionMenu *cmenu4)
++{
++ int i;
++ GtkWidget *menu1, *item1;
++ GtkWidget *menu2, *item2;
++ GtkWidget *menu3, *item3;
++ GtkWidget *menu4, *item4;
++
++ menu1 = gtk_menu_new();
++ gtk_option_menu_remove_menu(cmenu1);
++ gtk_option_menu_set_menu(cmenu1, menu1);
++
++ menu2 = gtk_menu_new();
++ gtk_option_menu_remove_menu(cmenu2);
++ gtk_option_menu_set_menu(cmenu2, menu2);
++
++ menu3 = gtk_menu_new();
++ gtk_option_menu_remove_menu(cmenu3);
++ gtk_option_menu_set_menu(cmenu3, menu3);
++
++ menu4 = gtk_menu_new();
++ gtk_option_menu_remove_menu(cmenu4);
++ gtk_option_menu_set_menu(cmenu4, menu4);
++
++ selected_id3_charset=0;
++ selected_output_charset=0;
++ selected_fs_charset=0;
++ selected_pl_charset=0;
++
++ for (i=0;charset_list[i];i++) {
++ item1 = gtk_menu_item_new_with_label(charset_list[i]);
++ gtk_signal_connect(GTK_OBJECT(item1), "activate", GTK_SIGNAL_FUNC(prefswin_charset_cb), GINT_TO_POINTER(i));
++ gtk_widget_show(item1);
++ gtk_menu_append(GTK_MENU(menu1), item1);
++ item2 = gtk_menu_item_new_with_label(charset_list[i]);
++ gtk_signal_connect(GTK_OBJECT(item2), "activate", GTK_SIGNAL_FUNC(prefswin_charset_cb), GINT_TO_POINTER(i+0xFFFF));
++ gtk_widget_show(item2);
++ gtk_menu_append(GTK_MENU(menu2), item2);
++ item3 = gtk_menu_item_new_with_label(charset_list[i]);
++ gtk_signal_connect(GTK_OBJECT(item3), "activate", GTK_SIGNAL_FUNC(prefswin_charset_cb), GINT_TO_POINTER(i+0x1FFFF));
++ gtk_widget_show(item3);
++ gtk_menu_append(GTK_MENU(menu3), item3);
++ item4 = gtk_menu_item_new_with_label(charset_list[i]);
++ gtk_signal_connect(GTK_OBJECT(item4), "activate", GTK_SIGNAL_FUNC(prefswin_charset_cb), GINT_TO_POINTER(i+0x3FFFF));
++ gtk_widget_show(item4);
++ gtk_menu_append(GTK_MENU(menu4), item4);
++ if ((cfg->charset_id3)&&(!strcmp(cfg->charset_id3,charset_list[i]))) {
++ selected_id3_charset=i;
++ }
++ if ((cfg->charset_output)&&(!strcmp(cfg->charset_output,charset_list[i]))) {
++ selected_output_charset=i;
++ }
++ if ((cfg->charset_fs)&&(!strcmp(cfg->charset_fs,charset_list[i]))) {
++ selected_fs_charset=i;
++ }
++ if ((cfg->charset_pl)&&(!strcmp(cfg->charset_pl,charset_list[i]))) {
++ selected_pl_charset=i;
++ }
++ }
++
++ gtk_option_menu_set_history(cmenu1, selected_id3_charset);
++ gtk_option_menu_set_history(cmenu2, selected_output_charset);
++ gtk_option_menu_set_history(cmenu3, selected_fs_charset);
++ gtk_option_menu_set_history(cmenu4, selected_pl_charset);
++}
++
++static void autocharset_options_add(Config *cfg, GtkOptionMenu *cmenu)
++{
++ int i;
++ GtkWidget *menu, *item;
++
++ menu = gtk_menu_new();
++ gtk_option_menu_remove_menu(cmenu);
++ gtk_option_menu_set_menu(cmenu, menu);
++
++ selected_autocharset=0;
++
++
++ for (i=0;i<autocharset_list_ni;i++) {
++ item = gtk_menu_item_new_with_label(autocharset_list[i].title);
++ gtk_signal_connect(GTK_OBJECT(item), "activate", GTK_SIGNAL_FUNC(prefswin_charset_cb), GINT_TO_POINTER(i+0x2FFFF));
++ gtk_widget_show(item);
++ gtk_menu_append(GTK_MENU(menu), item);
++ if ((cfg->autocharset)&&(cfg->autocharset==i)) {
++ selected_autocharset=i;
++ }
++ }
++
++ gtk_option_menu_set_history(cmenu, selected_autocharset);
++}
++
++void charset_create_prefs_window(Config *cfg, GtkNotebook *prefswin_notebook, GtkWidget *prefswin_fonts_vbox) {
++ GtkWidget *prefswin_encodings_vbox;
++ GtkWidget *prefswin_fonts_shade_frame;
++ GtkWidget *options_font_shade_hbox, *options_font_shade_vbox;
++ GtkWidget *prefswin_charset_frame, *prefswin_charset_vbox;
++ GtkWidget *prefswin_charset_list1, *prefswin_charset_list2, *prefswin_charset_list3, *prefswin_charset_list4;
++ GtkWidget *prefswin_charset_hbox1, *prefswin_charset_hbox2, *prefswin_charset_hbox3, *prefswin_charset_hbox4;
++ GtkWidget *prefswin_charset_label1, *prefswin_charset_label2, *prefswin_charset_label3, *prefswin_charset_label4;
++ GtkWidget *prefswin_autocharset_frame, *prefswin_autocharset_vbox;
++ GtkWidget *prefswin_autocharset_list;
++ GtkWidget *prefswin_autocharset_hbox;
++ GtkWidget *prefswin_autocharset_label;
++
++ prefswin_fonts_shade_frame = gtk_frame_new(_("Shade"));
++ gtk_container_set_border_width(GTK_CONTAINER(prefswin_fonts_shade_frame), 5);
++ gtk_box_pack_start(GTK_BOX(prefswin_fonts_vbox), prefswin_fonts_shade_frame, FALSE, FALSE, 0);
++ options_font_shade_vbox = gtk_vbox_new(FALSE, 5);
++ gtk_container_border_width(GTK_CONTAINER(options_font_shade_vbox), 5);
++ gtk_container_add(GTK_CONTAINER(prefswin_fonts_shade_frame), options_font_shade_vbox);
++ options_font_shade_hbox = gtk_hbox_new(FALSE, 5);
++ gtk_box_pack_start_defaults(GTK_BOX(options_font_shade_vbox), options_font_shade_hbox);
++ prefswin_options_font_shade_entry = gtk_entry_new();
++ gtk_box_pack_start(GTK_BOX(options_font_shade_hbox), prefswin_options_font_shade_entry, TRUE, TRUE, 0);
++ prefswin_options_font_shade_browse = gtk_button_new_with_label(_("Browse"));
++ gtk_signal_connect(GTK_OBJECT(prefswin_options_font_shade_browse), "clicked", GTK_SIGNAL_FUNC(prefswin_font_shade_browse_cb), NULL);
++ gtk_widget_set_usize(prefswin_options_font_shade_browse, 85, 17);
++ gtk_box_pack_start(GTK_BOX(options_font_shade_hbox), prefswin_options_font_shade_browse, FALSE, TRUE, 0);
++
++ prefswin_encodings_vbox = gtk_vbox_new(FALSE, 0);
++
++ prefswin_charset_frame = gtk_frame_new(_("Encodings"));
++ gtk_box_pack_start(GTK_BOX(prefswin_encodings_vbox), prefswin_charset_frame, FALSE, FALSE, 0);
++ gtk_container_border_width(GTK_CONTAINER(prefswin_charset_frame), 5);
++ prefswin_charset_vbox = gtk_vbox_new(FALSE, 3);
++ gtk_container_add(GTK_CONTAINER(prefswin_charset_frame), prefswin_charset_vbox);
++ gtk_container_border_width(GTK_CONTAINER(prefswin_charset_vbox), 5);
++
++ prefswin_charset_hbox1 = gtk_hbox_new(FALSE, 10);
++ gtk_container_add(GTK_CONTAINER(prefswin_charset_vbox), prefswin_charset_hbox1);
++ gtk_container_border_width(GTK_CONTAINER(prefswin_charset_hbox1), 5);
++ prefswin_charset_label1 = gtk_label_new(_("ID3 Encoding"));
++ gtk_widget_set_usize(prefswin_charset_label1, 120, 17);
++ gtk_box_pack_start(GTK_BOX(prefswin_charset_hbox1), prefswin_charset_label1, FALSE, FALSE, 0);
++ prefswin_charset_list1 = gtk_option_menu_new();
++ gtk_box_pack_start(GTK_BOX(prefswin_charset_hbox1), prefswin_charset_list1, TRUE, TRUE, 0);
++
++ prefswin_charset_hbox3 = gtk_hbox_new(FALSE, 10);
++ gtk_container_add(GTK_CONTAINER(prefswin_charset_vbox), prefswin_charset_hbox3);
++ gtk_container_border_width(GTK_CONTAINER(prefswin_charset_hbox3), 5);
++ prefswin_charset_label3 = gtk_label_new(_("FileSystem Encoding"));
++ gtk_widget_set_usize(prefswin_charset_label3, 120, 17);
++ gtk_box_pack_start(GTK_BOX(prefswin_charset_hbox3), prefswin_charset_label3, FALSE, FALSE, 0);
++ prefswin_charset_list3 = gtk_option_menu_new();
++ gtk_box_pack_start(GTK_BOX(prefswin_charset_hbox3), prefswin_charset_list3, TRUE, TRUE, 0);
++
++ prefswin_charset_hbox4 = gtk_hbox_new(FALSE, 10);
++ gtk_container_add(GTK_CONTAINER(prefswin_charset_vbox), prefswin_charset_hbox4);
++ gtk_container_border_width(GTK_CONTAINER(prefswin_charset_hbox4), 5);
++ prefswin_charset_label4 = gtk_label_new(_("Playlist Encoding"));
++ gtk_widget_set_usize(prefswin_charset_label4, 120, 17);
++ gtk_box_pack_start(GTK_BOX(prefswin_charset_hbox4), prefswin_charset_label4, FALSE, FALSE, 0);
++ prefswin_charset_list4 = gtk_option_menu_new();
++ gtk_box_pack_start(GTK_BOX(prefswin_charset_hbox4), prefswin_charset_list4, TRUE, TRUE, 0);
++
++ prefswin_charset_hbox2 = gtk_hbox_new(FALSE, 10);
++ gtk_container_add(GTK_CONTAINER(prefswin_charset_vbox), prefswin_charset_hbox2);
++ gtk_container_border_width(GTK_CONTAINER(prefswin_charset_hbox2), 5);
++ prefswin_charset_label2 = gtk_label_new(_("Output Encoding"));
++ gtk_widget_set_usize(prefswin_charset_label2, 120, 17);
++ gtk_box_pack_start(GTK_BOX(prefswin_charset_hbox2), prefswin_charset_label2, FALSE, FALSE, 0);
++ prefswin_charset_list2 = gtk_option_menu_new();
++ gtk_box_pack_start(GTK_BOX(prefswin_charset_hbox2), prefswin_charset_list2, TRUE, TRUE, 0);
++
++ charset_options_add(cfg, GTK_OPTION_MENU(prefswin_charset_list1),GTK_OPTION_MENU(prefswin_charset_list2),GTK_OPTION_MENU(prefswin_charset_list3), GTK_OPTION_MENU(prefswin_charset_list4));
++
++ prefswin_autocharset_frame = gtk_frame_new(_("Encoding Autoselection"));
++ gtk_box_pack_start(GTK_BOX(prefswin_encodings_vbox), prefswin_autocharset_frame, FALSE, FALSE, 0);
++ gtk_container_border_width(GTK_CONTAINER(prefswin_autocharset_frame), 5);
++ prefswin_autocharset_vbox = gtk_vbox_new(FALSE, 3);
++ gtk_container_add(GTK_CONTAINER(prefswin_autocharset_frame), prefswin_autocharset_vbox);
++ gtk_container_border_width(GTK_CONTAINER(prefswin_autocharset_vbox), 5);
++
++ prefswin_autocharset_hbox = gtk_hbox_new(FALSE, 10);
++ gtk_container_add(GTK_CONTAINER(prefswin_autocharset_vbox), prefswin_autocharset_hbox);
++ gtk_container_border_width(GTK_CONTAINER(prefswin_autocharset_hbox), 5);
++ prefswin_autocharset_label = gtk_label_new(_("Language"));
++ gtk_widget_set_usize(prefswin_autocharset_label, 120, 17);
++ gtk_box_pack_start(GTK_BOX(prefswin_autocharset_hbox), prefswin_autocharset_label, FALSE, FALSE, 0);
++ prefswin_autocharset_list = gtk_option_menu_new();
++ gtk_box_pack_start(GTK_BOX(prefswin_autocharset_hbox), prefswin_autocharset_list, TRUE, TRUE, 0);
++
++ autocharset_options_add(cfg, GTK_OPTION_MENU(prefswin_autocharset_list));
++
++ gtk_notebook_append_page(prefswin_notebook, prefswin_encodings_vbox, gtk_label_new(_("Encodings")));
++}
++
++void charset_prefswin_apply_changes(Config *cfg) {
++ if (cfg->charset_id3) g_free(cfg->charset_id3);
++ if (cfg->charset_output) g_free(cfg->charset_output);
++ if (cfg->charset_fs) g_free(cfg->charset_fs);
++ if (cfg->charset_pl) g_free(cfg->charset_pl);
++ cfg->charset_id3=g_strdup(charset_list[selected_id3_charset]);
++ cfg->charset_output=g_strdup(charset_list[selected_output_charset]);
++ cfg->charset_fs=g_strdup(charset_list[selected_fs_charset]);
++ cfg->charset_pl=g_strdup(charset_list[selected_pl_charset]);
++ xmms_charset_new_charsets(selected_id3_charset, selected_output_charset, selected_fs_charset, selected_pl_charset);
++ cfg->autocharset=selected_autocharset;
++ xmms_autocharset_new_mode(selected_autocharset);
++
++ if (cfg->shade_font) g_free(cfg->shade_font);
++ cfg->shade_font = g_strdup(gtk_entry_get_text(GTK_ENTRY(prefswin_options_font_shade_entry)));
++ textbox_set_xfont(playlistwin_sinfo, cfg->mainwin_use_xfont, cfg->shade_font);
++}
++
++void charset_show_prefs_window(Config *cfg) {
++ gtk_entry_set_text(GTK_ENTRY(prefswin_options_font_shade_entry), cfg->shade_font);
++}
++
++void charset_create_playlist_window(Config *cfg) {
++ textbox_set_xfont(playlistwin_sinfo, cfg->mainwin_use_xfont, cfg->shade_font);
++}
++/*****************************************************************************
++************************ Initialization / Deinitilization ********************
++*****************************************************************************/
++void charset_free(Config *cfg) {
++ xmms_charset_free();
++ xmms_autocharset_free();
++}
++
++void charset_init(Config *cfg) {
++ xmms_charset_init(cfg->charset_id3, cfg->charset_output, cfg->charset_fs, cfg->charset_pl);
++ xmms_autocharset_init(cfg->autocharset);
++}
++
++void charset_read_config(ConfigFile *cfgfile, Config *cfg) {
++ cfg->autocharset=0;
++ xmms_cfg_read_string(cfgfile, "xmms", "shade_font", &cfg->shade_font);
++ xmms_cfg_read_string(cfgfile, "xmms", "charset_id3", &cfg->charset_id3);
++ xmms_cfg_read_string(cfgfile, "xmms", "charset_output", &cfg->charset_output);
++ xmms_cfg_read_string(cfgfile, "xmms", "charset_fs", &cfg->charset_fs);
++ xmms_cfg_read_string(cfgfile, "xmms", "charset_pl", &cfg->charset_pl);
++ xmms_cfg_read_int(cfgfile,"xmms","autocharset",&cfg->autocharset);
++}
++
++void charset_write_config(ConfigFile *cfgfile, Config *cfg) {
++ xmms_cfg_write_string(cfgfile, "xmms", "shade_font", cfg->shade_font);
++ xmms_cfg_write_string(cfgfile, "xmms", "charset_id3", cfg->charset_id3);
++ xmms_cfg_write_string(cfgfile, "xmms", "charset_output", cfg->charset_output);
++ xmms_cfg_write_string(cfgfile, "xmms", "charset_fs", cfg->charset_fs);
++ xmms_cfg_write_string(cfgfile, "xmms", "charset_pl", cfg->charset_pl);
++ xmms_cfg_write_int(cfgfile,"xmms","autocharset",cfg->autocharset);
++}
++
++void charset_default_config(Config *cfg) {
++ if (cfg->shade_font && strlen(cfg->shade_font) == 0)
++ {
++ g_free(cfg->shade_font);
++ cfg->shade_font = NULL;
++ }
++ if (cfg->shade_font == NULL)
++ cfg->shade_font = g_strdup("-misc-fixed-medium-r-*-*-7-*");
++}
++
++/*****************************************************************************
++*********************************** Sort *************************************
++*****************************************************************************/
++gchar *saved_locale;
++
++static void charset_sort_null_entry(gpointer entry, gpointer data) {
++ ((PlaylistEntry*)entry)->sort=NULL;
++}
++
++static void charset_sort_free_entry(gpointer entry, gpointer data) {
++ if (((PlaylistEntry*)entry)->sort)
++ g_free(((PlaylistEntry*)entry)->sort);
++}
++
++void charset_sort_prepare(GList *playlist) {
++ g_list_foreach(playlist, charset_sort_null_entry, NULL);
++ saved_locale=g_strdup(setlocale(LC_COLLATE,NULL));
++ setlocale(LC_COLLATE,"UTF-8");
++}
++
++void charset_sort_free(GList *playlist) {
++ g_list_foreach(playlist, charset_sort_free_entry, NULL);
++ if (saved_locale) {
++ setlocale(LC_COLLATE,saved_locale);
++ g_free(saved_locale);
++ }
++}
++
++char *charset_sort_convert(PlaylistEntry *entry, gchar *title, int fs) {
++ char *str;
++ if (entry->sort) return entry->sort;
++ if (fs)
++ str=xmms_charset_recode_fs2utf(title,0,NULL);
++ else
++ str=xmms_charset_recode_id2utf(title,0,NULL);
++
++ if (!str)
++ entry->sort=g_strdup(title);
++ else {
++ strxfrm(xmms_charset_tmp,str,CHARSET_MAX_STRING_SIZE);
++// *(int*)(xmms_charset_tmp+(CHARSET_MAX_STRING_SIZE-sizeof(int)))=0;
++ xmms_charset_tmp[CHARSET_MAX_STRING_SIZE-1]=0;
++ entry->sort=g_strdup(xmms_charset_tmp);
++ g_free(str);
++ }
++
++ if (entry->sort)
++ return entry->sort;
++
++ return title;
++}
++
++/*****************************************************************************
++*********************************** Misc *************************************
++*****************************************************************************/
++
++void textbox_set_ctext(TextBox * tb, gchar * text)
++{
++ gchar *rtext;
++
++ lock_widget(tb);
++
++ rtext=xmms_charset_recode_id3(text,0,NULL);
++ if (rtext) text=rtext;
++ if (tb->tb_text)
++ {
++ if (!strcmp(text, tb->tb_text))
++ {
++ if (rtext) free(rtext);
++ unlock_widget(tb);
++ return;
++ }
++ g_free(tb->tb_text);
++ }
++
++ tb->tb_text = g_strdup(text);
++ if (rtext) free(rtext);
++
++ unlock_widget(tb);
++ draw_widget(tb);
++}
+diff -Naur xmms-1.2.8/xmms/charset.h xmms-1.2.8-new/xmms/charset.h
+--- xmms-1.2.8/xmms/charset.h 1970-01-01 01:00:00.000000000 +0100
++++ xmms-1.2.8-new/xmms/charset.h 2003-09-06 19:42:29.000000000 +0200
+@@ -0,0 +1,19 @@
++#include "libxmms/configfile.h"
++#include "libxmms/charset.h"
++void charset_create_prefs_window(Config *cfg, GtkNotebook *prefswin_notebook, GtkWidget *prefswin_fonts_vbox);
++void charset_show_prefs_window(Config *cfg);
++void charset_prefswin_apply_changes(Config *cfg);
++void charset_create_playlist_window(Config *cfg);
++
++void charset_init(Config *cfg);
++void charset_free(Config *cfg);
++
++void charset_read_config(ConfigFile *cfgfile, Config *cfg);
++void charset_default_config(Config *cfg);
++void charset_write_config(ConfigFile *cfgfile, Config *cfg);
++
++void charset_sort_prepare(GList *playlist);
++void charset_sort_free(GList *playlist);
++char *charset_sort_convert(PlaylistEntry *entry, gchar *title, int fs);
++
++void textbox_set_ctext(TextBox * tb, gchar * text);
+diff -Naur xmms-1.2.8/xmms/input.c xmms-1.2.8-new/xmms/input.c
+--- xmms-1.2.8/xmms/input.c 2003-07-14 15:24:28.000000000 +0200
++++ xmms-1.2.8-new/xmms/input.c 2003-09-06 19:42:29.000000000 +0200
+@@ -22,6 +22,7 @@
+ #include "libxmms/titlestring.h"
+ #include "libxmms/util.h"
+ #include "libxmms/xentry.h"
++#include "libxmms/charset.h"
+
+ static pthread_mutex_t vis_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+@@ -343,7 +344,7 @@
+ {
+ GList *node;
+ InputPlugin *ip = NULL;
+-
++
+ node = get_input_list();
+ while (node)
+ {
+@@ -371,8 +372,11 @@
+
+ (*title) = xmms_get_titlestring(xmms_get_gentitle_format(),
+ input);
+- if ( (*title) == NULL )
+- (*title) = g_strdup(input->file_name);
++ if ( (*title) == NULL ) {
++ (*title) = xmms_charset_recode_fs(input->file_name,0,NULL);
++ if (!*title) (*title) = g_strdup(input->file_name);
++ }
++
+ (*length) = -1;
+ g_free(temp);
+ g_free(input);
+diff -Naur xmms-1.2.8/xmms/main.c xmms-1.2.8-new/xmms/main.c
+--- xmms-1.2.8/xmms/main.c 2003-09-02 15:01:40.000000000 +0200
++++ xmms-1.2.8-new/xmms/main.c 2003-09-06 19:42:29.000000000 +0200
+@@ -35,6 +35,7 @@
+ #include "libxmms/xmmsctrl.h"
+ #include "libxmms/util.h"
+ #include "libxmms/dirbrowser.h"
++#include "charset.h"
+ #include "xmms_mini.xpm"
+
+ GtkWidget *mainwin, *mainwin_url_window = NULL, *mainwin_dir_browser = NULL;
+@@ -377,7 +378,6 @@
+
+ cfg.gentitle_format = NULL;
+
+-
+ filename = g_strconcat(g_get_home_dir(), "/.xmms/config", NULL);
+ cfgfile = xmms_cfg_open_file(filename);
+ if (cfgfile)
+@@ -428,6 +428,7 @@
+ xmms_cfg_read_boolean(cfgfile, "xmms", "use_fontsets", &cfg.use_fontsets);
+ xmms_cfg_read_boolean(cfgfile, "xmms", "mainwin_use_xfont", &cfg.mainwin_use_xfont);
+ xmms_cfg_read_string(cfgfile, "xmms", "mainwin_font", &cfg.mainwin_font);
++ charset_read_config(cfgfile,&cfg);
+ xmms_cfg_read_int(cfgfile, "xmms", "playlist_position", &cfg.playlist_position);
+ xmms_cfg_read_int(cfgfile, "xmms", "equalizer_x", &cfg.equalizer_x);
+ xmms_cfg_read_int(cfgfile, "xmms", "equalizer_y", &cfg.equalizer_y);
+@@ -514,6 +515,8 @@
+ if (cfg.eqpreset_extension == NULL)
+ cfg.eqpreset_extension = g_strdup("preset");
+
++ charset_default_config(&cfg);
++
+ g_free(filename);
+ }
+
+@@ -594,6 +597,7 @@
+ xmms_cfg_write_boolean(cfgfile, "xmms", "use_fontsets", cfg.use_fontsets);
+ xmms_cfg_write_boolean(cfgfile, "xmms", "mainwin_use_xfont", cfg.mainwin_use_xfont);
+ xmms_cfg_write_string(cfgfile, "xmms", "mainwin_font", cfg.mainwin_font);
++ charset_write_config(cfgfile,&cfg);
+ xmms_cfg_write_int(cfgfile, "xmms", "playlist_position", get_playlist_position());
+ /* dock_get_widget_pos(equalizerwin, &cfg.equalizer_x, &cfg.equalizer_y); */
+ xmms_cfg_write_int(cfgfile, "xmms", "equalizer_x", cfg.equalizer_x);
+@@ -893,6 +897,7 @@
+ gtk_timeout_remove(mainwin_timeout_tag);
+ util_set_cursor(NULL);
+ save_config();
++ charset_free(&cfg);
+ cleanup_ctrlsocket();
+ playlist_stop_get_info_thread();
+ playlist_clear();
+@@ -991,11 +996,11 @@
+
+ if ((text = input_get_info_text()) != NULL)
+ {
+- textbox_set_text(mainwin_info, text);
++ textbox_set_ctext(mainwin_info, text);
+ }
+ else if ((text = playlist_get_info_text()) != NULL)
+ {
+- textbox_set_text(mainwin_info, text);
++ textbox_set_ctext(mainwin_info, text);
+ g_free(text);
+ }
+ else
+@@ -3491,6 +3496,7 @@
+ #endif
+
+ read_config();
++ charset_init(&cfg);
+
+ if (geteuid() == 0)
+ {
+diff -Naur xmms-1.2.8/xmms/main.h xmms-1.2.8-new/xmms/main.h
+--- xmms-1.2.8/xmms/main.h 2002-10-06 18:35:27.000000000 +0200
++++ xmms-1.2.8-new/xmms/main.h 2003-09-06 19:42:29.000000000 +0200
+@@ -19,6 +19,7 @@
+ */
+ #ifndef MAIN_H
+ #define MAIN_H
++#include "libxmms/charset.h"
+
+ typedef enum
+ {
+@@ -60,6 +61,9 @@
+ gint mouse_change;
+ gboolean playlist_transparent;
+ gchar *gentitle_format;
++ gchar *charset_id3, *charset_output, *charset_fs, *charset_pl;
++ gint autocharset;
++ gchar *shade_font;
+ }
+ Config;
+
+diff -Naur xmms-1.2.8/xmms/Makefile.am xmms-1.2.8-new/xmms/Makefile.am
+--- xmms-1.2.8/xmms/Makefile.am 2003-08-11 15:47:41.000000000 +0200
++++ xmms-1.2.8-new/xmms/Makefile.am 2003-09-06 19:42:29.000000000 +0200
+@@ -56,7 +56,8 @@
+ getopt.c getopt1.c getopt.h \
+ urldecode.c urldecode.h \
+ dnd.h \
+-mkdtemp.c
++mkdtemp.c \
++charset.c charset.h
+
+ EXTRA_DIST= xmms_logo.xpm xmms_mini.xpm xmms.desktop xmms.wmconfig
+
+diff -Naur xmms-1.2.8/xmms/Makefile.in xmms-1.2.8-new/xmms/Makefile.in
+--- xmms-1.2.8/xmms/Makefile.in 2003-09-04 23:01:20.000000000 +0200
++++ xmms-1.2.8-new/xmms/Makefile.in 2003-09-06 19:42:29.000000000 +0200
+@@ -228,8 +228,8 @@
+ getopt.c getopt1.c getopt.h \
+ urldecode.c urldecode.h \
+ dnd.h \
+-mkdtemp.c
+-
++mkdtemp.c \
++charset.c charset.h
+
+ EXTRA_DIST = xmms_logo.xpm xmms_mini.xpm xmms.desktop xmms.wmconfig
+
+@@ -257,7 +257,7 @@
+ main.$(OBJEXT) skinwin.$(OBJEXT) prefswin.$(OBJEXT) \
+ playlistwin.$(OBJEXT) equalizer.$(OBJEXT) hints.$(OBJEXT) \
+ about.$(OBJEXT) sm.$(OBJEXT) getopt.$(OBJEXT) getopt1.$(OBJEXT) \
+-urldecode.$(OBJEXT) mkdtemp.$(OBJEXT)
++urldecode.$(OBJEXT) mkdtemp.$(OBJEXT) charset.$(OBJEXT)
+ xmms_DEPENDENCIES = $(top_builddir)/libxmms/libxmms.la
+ CFLAGS = @CFLAGS@
+ COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+diff -Naur xmms-1.2.8/xmms/playlist.c xmms-1.2.8-new/xmms/playlist.c
+--- xmms-1.2.8/xmms/playlist.c 2003-09-02 15:01:40.000000000 +0200
++++ xmms-1.2.8-new/xmms/playlist.c 2003-09-06 19:42:29.000000000 +0200
+@@ -20,6 +20,7 @@
+ #include "xmms.h"
+ #include <time.h>
+ #include "libxmms/util.h"
++#include "charset.h"
+ #include <sys/stat.h>
+ #include <unistd.h>
+
+@@ -939,6 +940,7 @@
+ char *playlist_get_info_text(void)
+ {
+ char *text, *title, *tmp, *numbers, *length;
++ char *ctitle;
+
+ PL_LOCK();
+ if (!playlist_position)
+@@ -947,10 +949,17 @@
+ return NULL;
+ }
+
+- if (playlist_position->title)
++ if (playlist_position->title) {
++ ctitle = xmms_charset_recode_id3(playlist_position->title,0,NULL);
++ if (ctitle) title=ctitle;
++ else
+ title = playlist_position->title;
+- else
++ } else {
++ ctitle = xmms_charset_recode_fsout(g_basename(playlist_position->filename),0,NULL);
++ if (ctitle) title=ctitle;
++ else
+ title = g_basename(playlist_position->filename);
++ }
+
+ /*
+ * If the user don't want numbers in the playlist, don't
+@@ -972,6 +981,7 @@
+ text = g_strdup_printf("%s%s%s", numbers, title, length);
+ g_free(numbers);
+ g_free(length);
++ if (ctitle) g_free(ctitle);
+
+ PL_UNLOCK();
+
+@@ -1007,6 +1017,7 @@
+ {
+ GList *node;
+ FILE *file;
++ gchar *fn, *cfn;
+
+ if ((file = fopen(filename, "w")) == NULL)
+ return FALSE;
+@@ -1025,10 +1036,13 @@
+ while (node)
+ {
+ PlaylistEntry *entry = node->data;
++ cfn=xmms_charset_recode_fs2pl(entry->filename,0,NULL);
++ if (cfn) fn=cfn;
++ else fn=entry->filename;
+ if (is_pls)
+ fprintf(file, "File%d=%s\n",
+ g_list_position(playlist, node) + 1,
+- entry->filename);
++ fn);
+ else
+ {
+ if (entry->title && cfg.use_pl_metadata)
+@@ -1043,8 +1057,9 @@
+ fprintf(file, "#EXTINF:%d,%s\n",
+ seconds, entry->title);
+ }
+- fprintf(file, "%s\n", entry->filename);
++ fprintf(file, "%s\n", fn);
+ }
++ if (cfn) g_free(cfn);
+ node = g_list_next(node);
+ }
+ PL_UNLOCK();
+@@ -1121,6 +1136,7 @@
+ {
+ FILE *file;
+ char *line, *ext;
++ char *cline;
+ guint entries = 0;
+ int linelen = 1024;
+ gboolean extm3u = FALSE;
+@@ -1147,8 +1163,10 @@
+ "playlist", key);
+ if (line != NULL)
+ {
+- playlist_load_ins_file(line, filename, pos,
++ cline = xmms_charset_recode_pl2fs(line,0,NULL);
++ playlist_load_ins_file(cline ? cline : line, filename, pos,
+ NULL, -1);
++ if (cline) g_free(cline);
+ entries++;
+ if (pos >= 0)
+ pos++;
+@@ -1215,7 +1233,9 @@
+ ext_info = NULL;
+ }
+
+- playlist_load_ins_file(line, filename, pos, ext_title, ext_len);
++ cline = xmms_charset_recode_pl2fs(line,linelen,NULL);
++ playlist_load_ins_file(cline ? cline : line, filename, pos, ext_title, ext_len);
++ if (cline) g_free(cline);
+
+ g_free(ext_title);
+ ext_title = NULL;
+@@ -1308,19 +1328,27 @@
+
+ if (entry->title == NULL && entry->length == -1)
+ {
+- if (playlist_get_info_entry(entry))
++ if (playlist_get_info_entry(entry)) {
++ title = xmms_charset_recode_id3(entry->title,0,NULL);
++ if (!title)
+ title = g_strdup(entry->title);
++ }
+
+ PL_UNLOCK();
+ }
+ else
+ {
++ title = xmms_charset_recode_id3(entry->title,0,NULL);
++ if (!title)
+ title = g_strdup(entry->title);
+ PL_UNLOCK();
+ }
+
+- if (title == NULL)
++ if (title == NULL) {
++ title = xmms_charset_recode_fsout(g_basename(filename),0,NULL);
++ if (!title)
+ title = g_strdup(g_basename(filename));
++ }
+
+ g_free(filename);
+
+@@ -1366,6 +1394,7 @@
+ static int playlist_sort_by_title_cmpfunc(PlaylistEntry * a, PlaylistEntry * b)
+ {
+ char *a_title, *b_title;
++ int a_fs=0, b_fs=0;
+
+ if (a->title)
+ a_title = a->title;
+@@ -1375,6 +1404,7 @@
+ a_title = strrchr(a->filename, '/') + 1;
+ else
+ a_title = a->filename;
++ a_fs=1;
+ }
+
+ if (b->title)
+@@ -1385,15 +1415,21 @@
+ b_title = strrchr(b->filename, '/') + 1;
+ else
+ b_title = b->filename;
+-
++ b_fs=1;
+ }
++
++ a_title=charset_sort_convert(a,a_title,a_fs);
++ b_title=charset_sort_convert(b,b_title,b_fs);
++
+ return strcasecmp(a_title, b_title);
+ }
+
+ void playlist_sort_by_title(void)
+ {
+ PL_LOCK();
++ charset_sort_prepare(playlist);
+ playlist = g_list_sort(playlist, (GCompareFunc) playlist_sort_by_title_cmpfunc);
++ charset_sort_free(playlist);
+ PL_UNLOCK();
+ }
+
+@@ -1411,13 +1447,18 @@
+ else
+ b_filename = b->filename;
+
++ a_filename=charset_sort_convert(a,a_filename,1);
++ b_filename=charset_sort_convert(b,b_filename,1);
++
+ return strcasecmp(a_filename, b_filename);
+ }
+
+ void playlist_sort_by_filename(void)
+ {
+ PL_LOCK();
++ charset_sort_prepare(playlist);
+ playlist = g_list_sort(playlist, (GCompareFunc) playlist_sort_by_filename_cmpfunc);
++ charset_sort_free(playlist);
+ PL_UNLOCK();
+ }
+
+@@ -1453,13 +1494,15 @@
+
+ static int playlist_sort_by_path_cmpfunc(PlaylistEntry * a, PlaylistEntry * b)
+ {
+- return playlist_sort_str_by_path_cmpfunc(a->filename, b->filename);
++ return playlist_sort_str_by_path_cmpfunc(charset_sort_convert(a,a->filename,1), charset_sort_convert(b,b->filename,1));
+ }
+
+ void playlist_sort_by_path(void)
+ {
+ PL_LOCK();
++ charset_sort_prepare(playlist);
+ playlist = g_list_sort(playlist, (GCompareFunc) playlist_sort_by_path_cmpfunc);
++ charset_sort_free(playlist);
+ PL_UNLOCK();
+ }
+
+@@ -1549,21 +1592,27 @@
+ void playlist_sort_selected_by_title(void)
+ {
+ PL_LOCK();
++ charset_sort_prepare(playlist);
+ playlist = playlist_sort_selected(playlist, (GCompareFunc) playlist_sort_by_title_cmpfunc);
++ charset_sort_free(playlist);
+ PL_UNLOCK();
+ }
+
+ void playlist_sort_selected_by_filename(void)
+ {
+ PL_LOCK();
++ charset_sort_prepare(playlist);
+ playlist = playlist_sort_selected(playlist, (GCompareFunc) playlist_sort_by_filename_cmpfunc);
++ charset_sort_free(playlist);
+ PL_UNLOCK();
+ }
+
+ void playlist_sort_selected_by_path(void)
+ {
+ PL_LOCK();
++ charset_sort_prepare(playlist);
+ playlist = playlist_sort_selected(playlist, (GCompareFunc) playlist_sort_by_path_cmpfunc);
++ charset_sort_free(playlist);
+ PL_UNLOCK();
+ }
+
+diff -Naur xmms-1.2.8/xmms/playlist.h xmms-1.2.8-new/xmms/playlist.h
+--- xmms-1.2.8/xmms/playlist.h 2003-06-09 15:22:10.000000000 +0200
++++ xmms-1.2.8-new/xmms/playlist.h 2003-09-06 19:42:29.000000000 +0200
+@@ -24,6 +24,7 @@
+ {
+ gchar *filename;
+ gchar *title;
++ gchar *sort;
+ gint length;
+ gboolean selected;
+ }
+diff -Naur xmms-1.2.8/xmms/playlist_list.c xmms-1.2.8-new/xmms/playlist_list.c
+--- xmms-1.2.8/xmms/playlist_list.c 2003-06-09 15:22:10.000000000 +0200
++++ xmms-1.2.8-new/xmms/playlist_list.c 2003-09-06 19:42:29.000000000 +0200
+@@ -287,6 +287,7 @@
+ {
+ GdkWChar *wtext;
+ int len, newlen;
++ gchar *rtext;
+ /*
+ * Convert the string to a wide character string to avoid
+ * destroying multibyte strings, when converting underscores,
+@@ -296,6 +297,10 @@
+ * Allocate some extra space, we might extend it by one
+ * character below
+ */
++
++ rtext = xmms_charset_recode_id3(text,0,NULL);
++ if (rtext) text = rtext;
++
+ wtext = g_malloc((strlen(text) + 3) * sizeof(GdkWChar));
+ len = gdk_mbstowcs(wtext, text, strlen(text) + 1);
+ if (len == -1)
+@@ -312,6 +317,7 @@
+ if (wtext[i] == L'_')
+ wtext[i] = L' ';
+ }
++ if (rtext) g_free(rtext);
+
+ if (cfg.convert_twenty && len > 2)
+ {
+@@ -361,6 +367,7 @@
+ {
+ int len;
+ char *tmp;
++ gchar *rtext;
+
+ if (cfg.convert_underscore)
+ while ((tmp = strchr(text, '_')) != NULL)
+@@ -384,7 +391,10 @@
+ text[len] = '\0';
+ }
+
++ rtext = xmms_charset_recode_id3(text,0,NULL);
++ if (rtext) text = rtext;
+ gdk_draw_text(pl->pl_widget.parent, font, pl->pl_widget.gc, pl->pl_widget.x, pl->pl_widget.y + line * pl->pl_fheight + font->ascent, text, len);
++ if (rtext) g_free(rtext);
+ }
+
+ void playlist_list_draw(Widget * w)
+@@ -396,6 +406,7 @@
+ int width, height;
+ char *text, *title;
+ int i, tw, max_first;
++ gchar *ctitle = NULL;
+
+ gc = pl->pl_widget.gc;
+ width = pl->pl_widget.width;
+@@ -453,8 +464,11 @@
+
+ if (entry->title)
+ title = entry->title;
+- else
+- title = g_basename(entry->filename);
++ else {
++ ctitle = xmms_charset_recode_fs(g_basename(entry->filename),0,NULL);
++ if (ctitle) title = ctitle;
++ else title = g_basename(entry->filename);
++ }
+
+ pos = playlist_get_queue_position(entry);
+
+@@ -497,6 +511,10 @@
+ playlist_list_draw_string(pl, playlist_list_font,
+ i - pl->pl_first, tw, text);
+ g_free(text);
++ if (ctitle) {
++ g_free(ctitle);
++ ctitle=NULL;
++ }
+ }
+ PL_UNLOCK();
+ }
+diff -Naur xmms-1.2.8/xmms/playlistwin.c xmms-1.2.8-new/xmms/playlistwin.c
+--- xmms-1.2.8/xmms/playlistwin.c 2003-04-04 19:42:51.000000000 +0200
++++ xmms-1.2.8-new/xmms/playlistwin.c 2003-09-06 20:24:02.000000000 +0200
+@@ -38,7 +38,8 @@
+ PButton *playlistwin_shade, *playlistwin_close;
+ static PlaylistSlider *playlistwin_slider = NULL;
+ static TextBox *playlistwin_time_min, *playlistwin_time_sec;
+-static TextBox *playlistwin_info, *playlistwin_sinfo;
++static TextBox *playlistwin_info;
++ TextBox *playlistwin_sinfo;
+ static SButton *playlistwin_srew, *playlistwin_splay;
+ static SButton *playlistwin_spause, *playlistwin_sstop;
+ static SButton *playlistwin_sfwd, *playlistwin_seject;
+@@ -1846,6 +1847,7 @@
+ static void playlistwin_create_widgets(void)
+ {
+ playlistwin_sinfo = create_textbox(&playlistwin_wlist, playlistwin_bg, playlistwin_gc, 4, 4, cfg.playlist_width - 35, FALSE, SKIN_TEXT);
++ charset_create_playlist_window(&cfg);
+ if (!cfg.playlist_shaded)
+ hide_widget(playlistwin_sinfo);
+ if (cfg.playlist_shaded)
+diff -Naur xmms-1.2.8/xmms/prefswin.c xmms-1.2.8-new/xmms/prefswin.c
+--- xmms-1.2.8/xmms/prefswin.c 2003-07-16 15:17:47.000000000 +0200
++++ xmms-1.2.8-new/xmms/prefswin.c 2003-09-06 19:42:29.000000000 +0200
+@@ -1,4 +1,4 @@
+-/* XMMS - Cross-platform multimedia player
++/* XMMS - Cdross-platform multimedia player
+ * Copyright (C) 1998-2001 Peter Alm, Mikael Alm, Olle Hallnas,
+ * Thomas Nilsson and 4Front Technologies
+ * Copyright (C) 1999-2001 Haavard Kvaalen
+@@ -20,6 +20,7 @@
+ #include "xmms.h"
+ #include "libxmms/util.h"
+ #include "libxmms/titlestring.h"
++#include "charset.h"
+
+ static GtkWidget *prefswin, *prefswin_notebook, *prefswin_ok;
+ static GtkWidget *prefswin_audio_ie_cbox;
+@@ -47,6 +48,7 @@
+ extern PButton *playlistwin_shade, *playlistwin_close, *equalizerwin_close;
+ extern PButton *mainwin_menubtn, *mainwin_minimize, *mainwin_shade, *mainwin_close;
+ extern TextBox *mainwin_info;
++extern TextBox *playlistwin_sinfo;
+ extern gboolean mainwin_focus, equalizerwin_focus, playlistwin_focus;
+
+ static gboolean is_opening = FALSE;
+@@ -351,6 +353,8 @@
+ cfg.pause_between_songs_time = CLAMP(atoi(gtk_entry_get_text(GTK_ENTRY(prefswin_options_pbs_entry))), 0, 1000);
+ cfg.mouse_change = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(prefswin_options_mouse_spin));
+
++ charset_prefswin_apply_changes(&cfg);
++
+ set_current_output_plugin(selected_oplugin);
+
+ equalizerwin_set_doublesize(cfg.doublesize && cfg.eq_doublesize_linked);
+@@ -384,7 +388,6 @@
+ textbox_set_scroll(mainwin_info, FALSE);
+ textbox_set_scroll(mainwin_info, TRUE);
+ }
+-
+ if (show_wm_old != cfg.show_wm_decorations)
+ prefswin_toggle_wm_decorations();
+
+@@ -1089,6 +1092,8 @@
+
+ gtk_notebook_append_page(GTK_NOTEBOOK(prefswin_notebook), prefswin_fonts_vbox, gtk_label_new(_("Fonts")));
+
++ charset_create_prefs_window(&cfg,GTK_NOTEBOOK(prefswin_notebook), prefswin_fonts_vbox);
++
+ /*
+ * Title page
+ */
+@@ -1358,6 +1363,8 @@
+ gtk_entry_set_text(GTK_ENTRY(prefswin_options_pbs_entry), temp);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(prefswin_options_mouse_spin), cfg.mouse_change);
+
++ charset_show_prefs_window(&cfg);
++
+ gtk_widget_show_all(prefswin);
+ gtk_widget_grab_default(prefswin_ok);
+
diff --git a/media-sound/xmms/files/xmms-1.2.8-sigterm.patch b/media-sound/xmms/files/xmms-1.2.8-sigterm.patch
new file mode 100644
index 000000000000..001b3df13209
--- /dev/null
+++ b/media-sound/xmms/files/xmms-1.2.8-sigterm.patch
@@ -0,0 +1,31 @@
+diff -Naur xmms-1.2.8/xmms/main.c xmms-1.2.8-new/xmms/main.c
+--- xmms-1.2.8/xmms/main.c 2003-09-02 15:01:40.000000000 +0200
++++ xmms-1.2.8-new/xmms/main.c 2003-09-06 18:15:15.000000000 +0200
+@@ -3343,6 +3343,18 @@
+ exit(1);
+ }
+
++/* Try to exit nicely when receiving a nice exit signal */
++void sigterm_handler(int sig)
++{
++ /* Original author of patch said that you should not use mainwin_quit_cb(),
++ * but his way deadlock xmms, and it anyhow just calls ctrlsocket stuff with
++ * with CMD_QUIT, which anyhow calls mainwin_quit_cb() in turn. This is not
++ * entirely clean, but works.
++ * <azarah@gentoo.org> (19 Jan 2003)
++ xmms_remote_quit(ctrlsocket_get_session_id()); */
++ mainwin_quit_cb();
++}
++
+ static gboolean pposition_configure(GtkWidget *w, GdkEventConfigure *event, gpointer data)
+ {
+ gint x,y;
+@@ -3473,6 +3485,8 @@
+ #endif
+
+ signal(SIGPIPE, SIG_IGN); /* for controlsocket.c */
++ signal(SIGTERM, sigterm_handler);
++ signal(SIGINT, sigterm_handler);
+ signal(SIGSEGV, segfault_handler);
+ g_thread_init(NULL);
+ if (!g_thread_supported())
diff --git a/media-sound/xmms/xmms-1.2.8-r1.ebuild b/media-sound/xmms/xmms-1.2.8-r1.ebuild
new file mode 100644
index 000000000000..dbbf773c9c40
--- /dev/null
+++ b/media-sound/xmms/xmms-1.2.8-r1.ebuild
@@ -0,0 +1,139 @@
+# Copyright 1999-2003 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+# $Header: /var/cvsroot/gentoo-x86/media-sound/xmms/xmms-1.2.8-r1.ebuild,v 1.1 2003/09/13 03:46:59 vapier Exp $
+
+inherit flag-o-matic eutils
+filter-flags -fforce-addr -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
+
+DESCRIPTION="X MultiMedia System"
+HOMEPAGE="http://www.xmms.org/"
+SRC_URI="http://www.xmms.org/files/1.2.x/${P}.tar.gz
+ mirror://gentoo/gentoo_ice.zip"
+
+LICENSE="GPL-2"
+SLOT="0"
+KEYWORDS="~x86 ~ppc ~sparc ~alpha ~hppa ~mips"
+IUSE="xml nls esd gnome opengl mmx oggvorbis 3dnow mikmod directfb ipv6 cjk"
+
+DEPEND="app-arch/unzip
+ =x11-libs/gtk+-1.2*
+ mikmod? ( >=media-libs/libmikmod-3.1.6 )
+ esd? ( >=media-sound/esound-0.2.22 )
+ xml? ( >=dev-libs/libxml-1.8.15 )
+ gnome? ( <gnome-base/gnome-panel-1.5.0 )
+ opengl? ( virtual/opengl )
+ oggvorbis? ( >=media-libs/libvorbis-1.0_beta4 )"
+RDEPEND="${DEPEND}
+ directfb? ( dev-libs/DirectFB )
+ nls? ( dev-util/intltool )"
+
+src_unpack() {
+ unpack ${P}.tar.gz
+ cd ${S}
+
+ # Patch to allow external programmes to have the "jump to" dialog box
+ epatch ${FILESDIR}/${P}-jump.patch
+
+ # Save playlist, etc on SIGTERM and SIGINT, bug #13604.
+ epatch ${FILESDIR}/${P}-sigterm.patch
+
+ # Patch for mpg123 to convert Japanese character code of MP3 tag info
+ # the Japanese patch and the Russian one overlap, so its one or the other
+ if use cjk; then
+ epatch ${FILESDIR}/${P}-mpg123j.patch
+ else
+ # add russian charset support
+ epatch ${FILESDIR}/${P}-russian-charset.patch
+ fi
+
+ # Add dynamic taste detection patch
+ epatch ${FILESDIR}/${P}-dtd.patch
+
+ if [ ! -f ${S}/config.rpath ] ; then
+ touch ${S}/config.rpath
+ chmod +x ${S}/config.rpath
+ fi
+
+ # Add /usr/local/share/xmms/Skins to the search path for skins
+ epatch ${FILESDIR}/${PN}-fhs-skinsdir.patch
+
+ # This patch passes audio output through the output plugin
+ # before recording via the diskwriter plugin
+ # http://forum.xmms.org/viewtopic.php?t=500&sid=c286e1c01fb924a2f81f519969f33764
+ epatch ${FILESDIR}/xmms-diskwriter-audio.patch
+
+ export WANT_AUTOCONF_2_5=1
+ for x in . libxmms ; do
+ cd ${S}/${x}
+ automake --gnu --add-missing --include-deps Makefile || die
+ done
+}
+
+src_compile() {
+ local myconf=""
+
+ # Allow configure to detect mipslinux systems
+ use mips && gnuconfig_update
+
+ if [ `use 3dnow` ] || [ `use mmx` ] ; then
+ myconf="${myconf} --enable-simd"
+ else
+ myconf="${myconf} --disable-simd"
+ fi
+
+ use xml || myconf="${myconf} --disable-cdindex"
+
+ econf \
+ --with-dev-dsp=/dev/sound/dsp \
+ --with-dev-mixer=/dev/sound/mixer \
+ `use_with gnome` \
+ `use_enable oggvorbis vorbis` \
+ `use_enable oggvorbis oggtest` \
+ `use_enable oggvorbis vorbistest` \
+ `use_enable esd` \
+ `use_enable esd esdtest` \
+ `use_enable mikmod` \
+ `use_enable mikmod mikmodtest` \
+ `use_with mikmod libmikmod` \
+ `use_enable opengl` \
+ `use_enable nls` \
+ `use_enable ipv6` \
+ ${myconf} \
+ || die
+
+ ### emake seems to break some compiles, please keep @ make
+ make || die
+}
+
+src_install() {
+ einstall \
+ incdir=${D}/usr/include \
+ sysdir=${D}/usr/share/applets/Multimedia \
+ GNOME_SYSCONFDIR=${D}/etc \
+ install || die "make install failed"
+
+ dodoc AUTHORS ChangeLog COPYING FAQ NEWS README TODO
+
+ keepdir /usr/share/xmms/Skins
+ insinto /usr/share/pixmaps/
+ donewins gnomexmms/gnomexmms.xpm xmms.xpm
+ doins xmms/xmms_logo.xpm
+ insinto /usr/share/pixmaps/mini
+ doins xmms/xmms_mini.xpm
+
+ insinto /etc/X11/wmconfig
+ donewins xmms/xmms.wmconfig xmms
+
+ if [ `use gnome` ] ; then
+ insinto /usr/share/gnome/apps/Multimedia
+ doins xmms/xmms.desktop
+ dosed "s:xmms_mini.xpm:mini/xmms_mini.xpm:" \
+ /usr/share/gnome/apps/Multimedia/xmms.desktop
+ else
+ rm ${D}/usr/share/man/man1/gnomexmms*
+ fi
+
+ # Add the sexy Gentoo Ice skin
+ insinto /usr/share/xmms/Skins
+ doins ${DISTDIR}/gentoo_ice.zip
+}