Move encoding and language stuff to their own directory
authorRémi Denis-Courmont <rem@videolan.org>
Wed, 22 Nov 2006 20:00:19 +0000 (20:00 +0000)
committerRémi Denis-Courmont <rem@videolan.org>
Wed, 22 Nov 2006 20:00:19 +0000 (20:00 +0000)
13 files changed:
modules/access/dvdnav.c
src/Makefile.am
src/input/es_out.c
src/misc/charset.c [deleted file]
src/misc/iso-639_def.h [deleted file]
src/misc/iso_lang.c [deleted file]
src/misc/strings.c [deleted file]
src/misc/unicode.c [deleted file]
src/text/charset.c [new file with mode: 0644]
src/text/iso-639_def.h [new file with mode: 0644]
src/text/iso_lang.c [new file with mode: 0644]
src/text/strings.c [new file with mode: 0644]
src/text/unicode.c [new file with mode: 0644]

index b424e46..85cf289 100644 (file)
@@ -47,7 +47,7 @@
 #include "iso_lang.h"
 
 /* FIXME we should find a better way than including that */
-#include "../../src/misc/iso-639_def.h"
+#include "../../src/text/iso-639_def.h"
 
 
 #include <dvdnav/dvdnav.h>
index 38a7b96..4dfa254 100644 (file)
@@ -314,21 +314,21 @@ SOURCES_libvlc_common = \
        network/httpd.c \
        network/rootwrap.c \
        network/tls.c \
-       misc/charset.c \
+       text/charset.c \
+       text/strings.c \
+       text/unicode.c \
+       text/iso_lang.c \
+       text/iso-639_def.h \
        misc/md5.c \
        misc/mtime.c \
        misc/block.c \
        misc/modules.c \
        misc/threads.c \
        misc/stats.c \
-       misc/strings.c \
-       misc/unicode.c \
        misc/cpu.c \
        misc/configuration.c \
        misc/configuration_chain.c \
        misc/image.c \
-       misc/iso_lang.c \
-       misc/iso-639_def.h \
        misc/messages.c \
        misc/objects.c \
        misc/variables.c \
index a07b5a1..78a0cfa 100644 (file)
@@ -38,7 +38,7 @@
 #include "vlc_playlist.h"
 #include "iso_lang.h"
 /* FIXME we should find a better way than including that */
-#include "../misc/iso-639_def.h"
+#include "../text/iso-639_def.h"
 
 /*****************************************************************************
  * Local prototypes
diff --git a/src/misc/charset.c b/src/misc/charset.c
deleted file mode 100644 (file)
index e33d355..0000000
+++ /dev/null
@@ -1,629 +0,0 @@
-/*****************************************************************************
- * charset.c: Locale's character encoding stuff.
- *****************************************************************************
- * See also unicode.c for Unicode to locale conversion helpers.
- *
- * Copyright (C) 2003-2006 the VideoLAN team
- * $Id$
- *
- * Authors: Derk-Jan Hartman <thedj at users.sf.net>
- *          Christophe Massiot
- *          Rémi Denis-Courmont
- *
- * vlc_current_charset() an adaption of mp_locale_charset():
- *
- *  Copyright (C) 2001-2003 The Mape Project
- *  Written by Karel Zak  <zakkr@zf.jcu.cz>.
- *
- * 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
- *****************************************************************************/
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <vlc/vlc.h>
-
-#if !defined WIN32
-# if HAVE_LANGINFO_CODESET
-#  include <langinfo.h>
-# endif
-# include <locale.h>
-#else
-# include <windows.h>
-#endif
-
-#ifdef __APPLE__
-#   include <errno.h>
-#   include <string.h>
-#endif
-
-#include "charset.h"
-
-typedef struct VLCCharsetAlias
-{
-    char *psz_alias, *psz_name;
-} VLCCharsetAlias;
-
-/*
- * The libcharset load all from external text file, but it's strange and
- * slow solution, we rather use array(s) compiled into source. In the
- * "good" libc this is not needful -- for example in linux.
- *
- * Please, put to this funtion exotic aliases only. The libc 'iconv' knows
- * a lot of basic aliases (check it first by iconv -l).
- *
- */
-#if (defined OS2 || !HAVE_LANGINFO_CODESET) && !defined WIN32
-static const char* vlc_encoding_from_language( const char *l )
-{
-    /* check for language (and perhaps country) codes */
-    if (strstr(l, "zh_TW")) return "Big5";
-    if (strstr(l, "zh_HK")) return "Big5HKSCS";   /* no MIME charset */
-    if (strstr(l, "zh")) return "GB2312";
-    if (strstr(l, "th")) return "TIS-620";
-    if (strstr(l, "ja")) return "EUC-JP";
-    if (strstr(l, "ko")) return "EUC-KR";
-    if (strstr(l, "ru")) return "KOI8-R";
-    if (strstr(l, "uk")) return "KOI8-U";
-    if (strstr(l, "pl") || strstr(l, "hr") ||
-        strstr(l, "hu") || strstr(l, "cs") ||
-        strstr(l, "sk") || strstr(l, "sl")) return "ISO-8859-2";
-    if (strstr(l, "eo") || strstr(l, "mt")) return "ISO-8859-3";
-    if (strstr(l, "lt") || strstr(l, "la")) return "ISO-8859-4";
-    if (strstr(l, "bg") || strstr(l, "be") ||
-        strstr(l, "mk") || strstr(l, "uk")) return "ISO-8859-5";
-    if (strstr(l, "ar")) return "ISO-8859-6";
-    if (strstr(l, "el")) return "ISO-8859-7";
-    if (strstr(l, "he") || strstr(l, "iw")) return "ISO-8859-8";
-    if (strstr(l, "tr")) return "ISO-8859-9";
-    if (strstr(l, "th")) return "ISO-8859-11";
-    if (strstr(l, "lv")) return "ISO-8859-13";
-    if (strstr(l, "cy")) return "ISO-8859-14";
-    if (strstr(l, "et")) return "ISO-8859-15"; /* all latin1 could be iso15 as well */
-    if (strstr(l, "ro")) return "ISO-8859-2";   /* or ISO-8859-16 */
-    if (strstr(l, "am") || strstr(l, "vi")) return "UTF-8";
-    /* We don't know. This ain't working go to default. */
-    return "ISO-8859-1";
-}
-#endif
-
-static const char* vlc_charset_aliases( const char *psz_name )
-{
-    VLCCharsetAlias     *a;
-
-#if defined WIN32
-    VLCCharsetAlias aliases[] =
-    {
-        { "CP936",      "GBK" },
-        { "CP1361",     "JOHAB" },
-        { "CP20127",    "ASCII" },
-        { "CP20866",    "KOI8-R" },
-        { "CP21866",    "KOI8-RU" },
-        { "CP28591",    "ISO-8859-1" },
-        { "CP28592",    "ISO-8859-2" },
-        { "CP28593",    "ISO-8859-3" },
-        { "CP28594",    "ISO-8859-4" },
-        { "CP28595",    "ISO-8859-5" },
-        { "CP28596",    "ISO-8859-6" },
-        { "CP28597",    "ISO-8859-7" },
-        { "CP28598",    "ISO-8859-8" },
-        { "CP28599",    "ISO-8859-9" },
-        { "CP28605",    "ISO-8859-15" },
-        { NULL,         NULL }
-    };
-#elif defined (SYS_AIX)
-    VLCCharsetAlias aliases[] =
-    {
-        { "IBM-850",    "CP850" },
-        { "IBM-856",    "CP856" },
-        { "IBM-921",    "ISO-8859-13" },
-        { "IBM-922",    "CP922" },
-        { "IBM-932",    "CP932" },
-        { "IBM-943",    "CP943" },
-        { "IBM-1046",   "CP1046" },
-        { "IBM-1124",   "CP1124" },
-        { "IBM-1129",   "CP1129" },
-        { "IBM-1252",   "CP1252" },
-        { "IBM-EUCCN",  "GB2312" },
-        { "IBM-EUCJP",  "EUC-JP" },
-        { "IBM-EUCKR",  "EUC-KR" },
-        { "IBM-EUCTW",  "EUC-TW" },
-        { NULL, NULL }
-    };
-#elif defined (SYS_HPUX)
-    VLCCharsetAlias aliases[] =
-    {
-        { "ROMAN8",     "HP-ROMAN8" },
-        { "ARABIC8",    "HP-ARABIC8" },
-        { "GREEK8",     "HP-GREEK8" },
-        { "HEBREW8",    "HP-HEBREW8" },
-        { "TURKISH8",   "HP-TURKISH8" },
-        { "KANA8",      "HP-KANA8" },
-        { "HP15CN",     "GB2312" },
-        { NULL, NULL }
-    };
-#elif defined (SYS_IRIX)
-    VLCCharsetAlias aliases[] =
-    {
-        { "EUCCN",      "GB2312" },
-        { NULL, NULL }
-    };
-#elif defined (SYS_OSF)
-    VLCCharsetAlias aliases[] =
-    {
-        { "KSC5601",    "CP949" },
-        { "SDECKANJI",  "EUC-JP" },
-        { "TACTIS",     "TIS-620" },
-        { NULL, NULL }
-    };
-#elif defined (SYS_SOLARIS)
-    VLCCharsetAlias aliases[] =
-    {
-        { "646", "ASCII" },
-        { "CNS11643",   "EUC-TW" },
-        { "5601",       "EUC-KR" },
-        { "JOHAP92",    "JOHAB" },
-        { "PCK", "SHIFT_JIS" },
-        { "2533",       "TIS-620" },
-        { NULL, NULL }
-    };
-#elif defined (SYS_BSD)
-    VLCCharsetAlias aliases[] =
-    {
-        { "646", " ASCII" },
-        { "EUCCN", "GB2312" },
-        { NULL, NULL }
-    };
-#else
-    VLCCharsetAlias aliases[] = {{NULL, NULL}};
-#endif
-
-    for (a = aliases; a->psz_alias; a++)
-        if (strcasecmp (a->psz_alias, psz_name) == 0)
-            return a->psz_name;
-
-    /* we return original name beacuse iconv() probably will know
-     * something better about name if we don't know it :-) */
-    return psz_name;
-}
-
-/* Returns charset from "language_COUNTRY.charset@modifier" string */
-#if (defined OS2 || !HAVE_LANGINFO_CODESET) && !defined WIN32
-static void vlc_encoding_from_locale( char *psz_locale, char *psz_charset )
-{
-    char *psz_dot = strchr( psz_locale, '.' );
-
-    if( psz_dot != NULL )
-    {
-        const char *psz_modifier;
-
-        psz_dot++;
-
-        /* Look for the possible @... trailer and remove it, if any.  */
-        psz_modifier = strchr( psz_dot, '@' );
-
-        if( psz_modifier == NULL )
-        {
-            strcpy( psz_charset, psz_dot );
-            return;
-        }
-        if( 0 < ( psz_modifier - psz_dot )
-             && ( psz_modifier - psz_dot ) < 2 + 10 + 1 )
-        {
-            memcpy( psz_charset, psz_dot, psz_modifier - psz_dot );
-            psz_charset[ psz_modifier - psz_dot ] = '\0';
-            return;
-        }
-    }
-    /* try language mapping */
-    strcpy( psz_charset, vlc_encoding_from_language( psz_locale ) );
-}
-#endif
-
-vlc_bool_t vlc_current_charset( char **psz_charset )
-{
-    const char *psz_codeset;
-
-#if !(defined WIN32 || defined OS2 || defined __APPLE__)
-
-# if HAVE_LANGINFO_CODESET
-    /* Most systems support nl_langinfo( CODESET ) nowadays.  */
-    psz_codeset = nl_langinfo( CODESET );
-    if( !strcmp( psz_codeset, "ANSI_X3.4-1968" ) )
-        psz_codeset = "ASCII";
-# else
-    /* On old systems which lack it, use setlocale or getenv.  */
-    const char *psz_locale = NULL;
-    char buf[2 + 10 + 1];
-
-    /* But most old systems don't have a complete set of locales.  Some
-     * (like SunOS 4 or DJGPP) have only the C locale.  Therefore we don't
-     * use setlocale here; it would return "C" when it doesn't support the
-     * locale name the user has set. Darwin's setlocale is broken. */
-#  if HAVE_SETLOCALE && !__APPLE__
-    psz_locale = setlocale( LC_ALL, NULL );
-#  endif
-    if( psz_locale == NULL || psz_locale[0] == '\0' )
-    {
-        psz_locale = getenv( "LC_ALL" );
-        if( psz_locale == NULL || psz_locale[0] == '\0' )
-        {
-            psz_locale = getenv( "LC_CTYPE" );
-            if( psz_locale == NULL || psz_locale[0] == '\0')
-                psz_locale = getenv( "LANG" );
-        }
-    }
-
-    /* On some old systems, one used to set locale = "iso8859_1". On others,
-     * you set it to "language_COUNTRY.charset". Darwin only has LANG :( */
-    vlc_encoding_from_locale( (char *)psz_locale, buf );
-    psz_codeset =  buf;
-# endif /* HAVE_LANGINFO_CODESET */
-
-#elif defined __APPLE__
-
-    /* Darwin is always using UTF-8 internally. */
-    psz_codeset = "UTF-8";
-
-#elif defined WIN32
-
-    char buf[2 + 10 + 1];
-
-    /* Woe32 has a function returning the locale's codepage as a number.  */
-    snprintf( buf, sizeof( buf ), "CP%u", GetACP() );
-    psz_codeset = buf;
-
-#elif defined OS2
-
-    const char *psz_locale;
-    char buf[2 + 10 + 1];
-    ULONG cp[3];
-    ULONG cplen;
-
-    /* Allow user to override the codeset, as set in the operating system,
-     * with standard language environment variables. */
-    psz_locale = getenv( "LC_ALL" );
-    if( psz_locale == NULL || psz_locale[0] == '\0' )
-    {
-        psz+locale = getenv( "LC_CTYPE" );
-        if( psz_locale == NULL || locale[0] == '\0' )
-            locale = getenv( "LANG" );
-    }
-    if( psz_locale != NULL && psz_locale[0] != '\0' )
-        vlc_encoding_from_locale( psz_locale, buf );
-        psz_codeset = buf;
-    else
-    {
-        /* OS/2 has a function returning the locale's codepage as a number. */
-        if( DosQueryCp( sizeof( cp ), cp, &cplen ) )
-            psz_codeset = "";
-        else
-        {
-            snprintf( buf, sizeof( buf ), "CP%u", cp[0] );
-            psz_codeset = buf;
-        }
-    }
-#endif
-    if( psz_codeset == NULL )
-        /* The canonical name cannot be determined. */
-        psz_codeset = "";
-    else
-        psz_codeset = vlc_charset_aliases( psz_codeset );
-
-    /* Don't return an empty string.  GNU libc and GNU libiconv interpret
-     * the empty string as denoting "the locale's character encoding",
-     * thus GNU libiconv would call this function a second time. */
-    if( psz_codeset[0] == '\0' )
-    {
-        /* Last possibility is 'CHARSET' enviroment variable */
-        if( !( psz_codeset = getenv( "CHARSET" ) ) )
-            psz_codeset = "ISO-8859-1";
-    }
-
-    if( psz_charset )
-        *psz_charset = strdup(psz_codeset);
-
-    if( !strcasecmp(psz_codeset, "UTF8") || !strcasecmp(psz_codeset, "UTF-8") )
-        return VLC_TRUE;
-
-    return VLC_FALSE;
-}
-
-
-char *vlc_fix_readdir( const char *psz_string )
-{
-#ifdef __APPLE__
-    vlc_iconv_t hd = vlc_iconv_open( "UTF-8", "UTF-8-MAC" );
-
-    if (hd != (vlc_iconv_t)(-1))
-    {
-        const char *psz_in = psz_string;
-        size_t i_in = strlen(psz_in);
-        size_t i_out = i_in * 2;
-        char *psz_utf8 = malloc(i_out + 1);
-        char *psz_out = psz_utf8;
-
-        size_t i_ret = vlc_iconv (hd, &psz_in, &i_in, &psz_out, &i_out);
-        vlc_iconv_close (hd);
-        if( i_ret == (size_t)(-1) || i_in )
-        {
-            free( psz_utf8 );
-            return strdup( psz_string );
-        }
-
-        *psz_out = '\0';
-        return psz_utf8;
-    }
-#else
-    return strdup( psz_string );
-#endif
-}
-
-
-static inline int locale_match (const char *tab, const char *locale)
-{
-    for (;*tab; tab += 2)
-        if (memcmp (tab, locale, 2) == 0)
-            return 0;
-    return 1;
-}
-
-
-/**
- * @return a fallback characters encoding to be used, given a locale.
- */
-static const char *FindFallbackEncoding (const char *locale)
-{
-    if ((locale == NULL) || (strlen (locale) < 2))
-        return "ASCII";
-
-
-    /*** The ISO-8859 series (anything but Asia) ***/
-    // Latin-1 Western-European languages (ISO-8859-1)
-    static const char western[] =
-        "aa" "af" "an" "br" "ca" "da" "de" "en" "es" "et" "eu" "fi" "fo" "fr"
-        "ga" "gd" "gl" "gv" "id" "is" "it" "kl" "kw" "mg" "ms" "nb" "nl" "nn"
-        "no" "oc" "om" "pt" "so" "sq" "st" "sv" "tl" "uz" "wa" "xh" "zu"
-        "eo" "mt" "cy";
-    if (!locale_match (western, locale))
-        return "CP1252"; // Compatible Microsoft superset
-
-    // Latin-2 Slavic languages (ISO-8859-2)
-    static const char slavic[] = "bs" "cs" "hr" "hu" "pl" "ro" "sk" "sl";
-    if (!locale_match (slavic, locale))
-        return "CP1250"; // CP1250 is more common, but incompatible
-
-    // Latin-3 Southern European languages (ISO-8859-3)
-    // "eo" and "mt" -> Latin-1 instead, I presume(?).
-    // "tr" -> ISO-8859-9 instead
-
-    // Latin-4 North-European languages (ISO-8859-4)
-    // -> Latin-1 instead
-
-    /* Cyrillic alphabet languages (ISO-8859-5) */
-    static const char cyrillic[] = "be" "bg" "mk" "ru" "sr";
-    if (!locale_match (cyrillic, locale))
-        return "CP1251"; // KOI8, ISO-8859-5 and CP1251 are incompatible(?)
-
-    /* Arabic (ISO-8859-6) */
-    if (!locale_match ("ar", locale))
-        // FIXME: someone check if we should return CP1256 or ISO-8859-6
-        return "CP1256"; // CP1256 is(?) more common, but incompatible(?)
-
-    /* Greek (ISO-8859-7) */
-    if (!locale_match ("el", locale))
-        // FIXME: someone check if we should return CP1253 or ISO-8859-7
-        return "CP1253"; // CP1253 is(?) more common and less incompatible
-
-    /* Hebrew (ISO-8859-8) */
-    if (!locale_match ("he" "iw" "yi", locale))
-        return "CP1255"; // Compatible Microsoft superset
-
-    /* Latin-5 Turkish (ISO-8859-9) */
-    if (!locale_match ("tr" "ku", locale))
-        return "CP1254"; // Compatible Microsoft superset
-
-    /* Latin-6 “North-European” languages (ISO-8859-10) */
-    /* It is so much north European that glibc only uses that for Luganda
-     * which is spoken in Uganda... unless someone complains, I'm not
-     * using this one; let's fallback to CP1252 here. */
-
-    // ISO-8859-11 does arguably not exist. Thai is handled below.
-
-    // ISO-8859-12 really doesn't exist.
-
-    // Latin-7 Baltic languages (ISO-8859-13)
-    if (!locale_match ("lt" "lv" "mi", locale))
-        // FIXME: mi = New Zealand, doesn't sound baltic!
-        return "CP1257"; // Compatible Microsoft superset
-
-    // Latin-8 Celtic languages (ISO-8859-14)
-    // "cy" -> use Latin-1 instead (most likely English or French)
-
-    // Latin-9 (ISO-8859-15) -> see Latin-1
-
-    // Latin-10 (ISO-8859-16) does not seem to be used
-
-    /*** KOI series ***/
-    // For Russian, we use CP1251
-    if (!locale_match ("uk", locale))
-        return "KOI8-U";
-
-    if (!locale_match ("tg", locale))
-        return "KOI8-T";
-
-    /*** Asia ***/
-    // Japanese
-    if (!locale_match ("jp", locale))
-        return "SHIFT-JIS"; // Shift-JIS is way more common than EUC-JP
-
-    // Korean
-    if (!locale_match ("ko", locale))
-        return "EUC-KR";
-
-    // Thai
-    if (!locale_match ("th", locale))
-        return "TIS-620";
-
-    // Vietnamese (FIXME: more infos needed)
-    if (!locale_match ("vt", locale))
-        /* VISCII is probably a bad idea as it is not extended ASCII */
-        /* glibc has TCVN5712-1 */
-        return "CP1258";
-
-    /* Kazakh (FIXME: more infos needed) */
-    if (!locale_match ("kk", locale))
-        return "PT154";
-
-    // Chinese. The politically incompatible character sets.
-    if (!locale_match ("zh", locale))
-    {
-        if ((strlen (locale) >= 5) && (locale[2] != '_'))
-            locale += 3;
-
-        // Hong Kong
-        if (!locale_match ("HK", locale))
-            return "BIG5-HKSCS"; /* FIXME: use something else? */
-
-        // Taiwan island
-        if (!locale_match ("TW", locale))
-            return "BIG5";
-
-        // People's Republic of China and Singapore
-        /*
-         * GB18030 can represent any Unicode code point
-         * (like UTF-8), while remaining compatible with GBK
-         * FIXME: is it compatible with GB2312? if not, should we
-         * use GB2312 instead?
-         */
-        return "GB18030";
-    }
-
-    return "ASCII";
-}
-
-/**
- * GetFallbackEncoding() suggests an encoding to be used for non UTF-8
- * text files accord to the system's local settings. It is only a best
- * guess.
- */
-const char *GetFallbackEncoding( void )
-{
-#ifndef WIN32
-    const char *psz_lang = NULL;
-
-    /* Some systems (like Darwin, SunOS 4 or DJGPP) have only the C locale.
-     * Therefore we don't use setlocale here; it would return "C". */
-#  if defined (HAVE_SETLOCALE) && !defined ( __APPLE__)
-    psz_lang = setlocale( LC_ALL, NULL );
-#  endif
-    if( psz_lang == NULL || psz_lang[0] == '\0' )
-    {
-        psz_lang = getenv( "LC_ALL" );
-        if( psz_lang == NULL || psz_lang == '\0' )
-        {
-            psz_lang = getenv( "LC_CTYPE" );
-            if( psz_lang == NULL || psz_lang[0] == '\0')
-                psz_lang = getenv( "LANG" );
-        }
-    }
-
-    return FindFallbackEncoding( psz_lang );
-#else
-    /*
-     * This should be thread-safe given GetACP() should always return
-     * the same result.
-     */
-    static char buf[2 + 10 + 1] = "";
-
-    if( buf[0] == 0 )
-        snprintf( buf, sizeof( buf ), "CP%u", GetACP() );
-    return buf;
-#endif
-}
-
-/**
- * There are two decimal separators in the computer world-wide locales:
- * dot (which is the american default), and comma (which is used in France,
- * the country with the most VLC developers, among others).
- *
- * i18n_strtod() has the same prototype as ANSI C strtod() but it accepts
- * either decimal separator when deserializing the string to a float number,
- * independant of the local computer setting.
- */
-double i18n_strtod( const char *str, char **end )
-{
-    char *end_buf, e;
-    double d;
-
-    if( end == NULL )
-        end = &end_buf;
-    d = strtod( str, end );
-
-    e = **end;
-    if(( e == ',' ) || ( e == '.' ))
-    {
-        char dup[strlen( str ) + 1];
-        strcpy( dup, str );
-
-        if( dup == NULL )
-            return d;
-
-        dup[*end - str] = ( e == ',' ) ? '.' : ',';
-        d = strtod( dup, end );
-    }
-    return d;
-}
-
-/**
- * i18n_atof() has the same prototype as ANSI C atof() but it accepts
- * either decimal separator when deserializing the string to a float number,
- * independant of the local computer setting.
- */
-double i18n_atof( const char *str )
-{
-    return i18n_strtod( str, NULL );
-}
-
-
-/**
- * us_strtod() has the same prototype as ANSI C strtod() but it expects
- * a dot as decimal separator regardless of the system locale.
- */
-double us_strtod( const char *str, char **end )
-{
-    char dup[strlen( str ) + 1], *ptr;
-    double d;
-    strcpy( dup, str );
-
-    ptr = strchr( dup, ',' );
-    if( ptr != NULL )
-        *ptr = '\0';
-
-    d = strtod( dup, &ptr );
-    if( end != NULL )
-        *end = (char *)&str[ptr - dup];
-
-    return d;
-}
-
-/**
- * us_atof() has the same prototype as ANSI C atof() but it expects a dot
- * as decimal separator, regardless of the system locale.
- */
-double us_atof( const char *str )
-{
-    return us_strtod( str, NULL );
-}
-
diff --git a/src/misc/iso-639_def.h b/src/misc/iso-639_def.h
deleted file mode 100644 (file)
index 829ccf6..0000000
+++ /dev/null
@@ -1,205 +0,0 @@
-/*****************************************************************************
- * iso_lang.h: languages codes and abbreviations
- *****************************************************************************
- * Copyright (C) 1998-2004 the VideoLAN team
- * $Id$
- *
- * This is used in iso_lang.cpp and is taken from the GNU glibc 2.2.5
- * tarball. It has been partially completed with native language names.
- * Authors: Stéphane Borel <stef@via.ecp.fr>
- *          Arnaud de Bossoreille de Ribou <bozo@via.ecp.fr>
- *
- * 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
- *****************************************************************************/
-
-/* Define the languages codes and abbreviations according to ISO 639-[12].
- * Format is:( "English name", 639-1-code, 639-2/T-code, 639-2/B-code)
- * If you find something missing or wrong contact <bug-glibc@gnu.org> */
-
-/* Some spellings were converted to pure ASCII:
- *  Provençal -> Provencal
- *  Volapk -> Volapuk
- *  Bokm? -> Bokmaal */
-
-static const iso639_lang_t p_languages[] =
-{
-    { N_( "Afar" ),             "",            "aa", "aar", "aar" },
-    { N_( "Abkhazian" ),        "",            "ab", "abk", "abk" },
-    { N_( "Afrikaans" ),        "",            "af", "afr", "afr" },
-    { N_( "Albanian" ),         "",            "sq", "sqi", "alb" },
-    { N_( "Amharic" ),          "",            "am", "amh", "amh" },
-    { N_( "Arabic" ),           "",            "ar", "ara", "ara" },
-    { N_( "Armenian" ),         "",            "hy", "hye", "arm" },
-    { N_( "Assamese" ),         "",            "as", "asm", "asm" },
-    { N_( "Avestan" ),          "",            "ae", "ave", "ave" },
-    { N_( "Aymara" ),           "",            "ay", "aym", "aym" },
-    { N_( "Azerbaijani" ),      "",            "az", "aze", "aze" },
-    { N_( "Bashkir" ),          "",            "ba", "bak", "bak" },
-    { N_( "Basque" ),           "",            "eu", "eus", "baq" },
-    { N_( "Belarusian" ),       "",            "be", "bel", "bel" },
-    { N_( "Bengali" ),          "",            "bn", "ben", "ben" },
-    { N_( "Bihari" ),           "",            "bh", "bih", "bih" },
-    { N_( "Bislama" ),          "",            "bi", "bis", "bis" },
-    { N_( "Bosnian" ),          "",            "bs", "bos", "bos" },
-    { N_( "Breton" ),           "",            "br", "bre", "bre" },
-    { N_( "Bulgarian" ),        "",            "bg", "bul", "bul" },
-    { N_( "Burmese" ),          "",            "my", "mya", "bur" },
-    { N_( "Catalan" ),          "Catala",      "ca", "cat", "cat" },
-    { N_( "Chamorro" ),         "",            "ch", "cha", "cha" },
-    { N_( "Chechen" ),          "",            "ce", "che", "che" },
-    { N_( "Chinese" ),          "",            "zh", "zho", "chi" },
-    { N_( "Church Slavic" ),    "",            "cu", "chu", "chu" },
-    { N_( "Chuvash" ),          "",            "cv", "chv", "chv" },
-    { N_( "Cornish" ),          "",            "kw", "cor", "cor" },
-    { N_( "Corsican" ),         "",            "co", "cos", "cos" },
-    { N_( "Czech" ),            "",            "cs", "ces", "cze" },
-    { N_( "Danish" ),           "Dansk",       "da", "dan", "dan" },
-    { N_( "Dutch" ),            "Nederlands",  "nl", "nld", "dut" },
-    { N_( "Dzongkha" ),         "",            "dz", "dzo", "dzo" },
-    { N_( "English" ),          "English",     "en", "eng", "eng" },
-    { N_( "Esperanto" ),        "",            "eo", "epo", "epo" },
-    { N_( "Estonian" ),         "",            "et", "est", "est" },
-    { N_( "Faroese" ),          "",            "fo", "fao", "fao" },
-    { N_( "Fijian" ),           "",            "fj", "fij", "fij" },
-    { N_( "Finnish" ),          "Suomi",       "fi", "fin", "fin" },
-    { N_( "French" ),           "Francais",    "fr", "fra", "fre" },
-    { N_( "Frisian" ),          "",            "fy", "fry", "fry" },
-    { N_( "Georgian" ),         "",            "ka", "kat", "geo" },
-    { N_( "German" ),           "Deutsch",     "de", "deu", "ger" },
-    { N_( "Gaelic (Scots)" ),   "",            "gd", "gla", "gla" },
-    { N_( "Irish" ),            "",            "ga", "gle", "gle" },
-    { N_( "Gallegan" ),         "",            "gl", "glg", "glg" },
-    { N_( "Manx" ),             "",            "gv", "glv", "glv" },
-    { N_( "Greek, Modern ()" ), "",            "el", "gre", "ell" },
-    { N_( "Guarani" ),          "",            "gn", "grn", "grn" },
-    { N_( "Gujarati" ),         "",            "gu", "guj", "guj" },
-    { N_( "Hebrew" ),           "",            "he", "heb", "heb" },
-    { N_( "Herero" ),           "",            "hz", "her", "her" },
-    { N_( "Hindi" ),            "",            "hi", "hin", "hin" },
-    { N_( "Hiri Motu" ),        "",            "ho", "hmo", "hmo" },
-    { N_( "Hungarian" ),        "Magyar",      "hu", "hun", "hun" },
-    { N_( "Icelandic" ),        "Islenska",    "is", "isl", "ice" },
-    { N_( "Inuktitut" ),        "",            "iu", "iku", "iku" },
-    { N_( "Interlingue" ),      "",            "ie", "ile", "ile" },
-    { N_( "Interlingua" ),      "",            "ia", "ina", "ina" },
-    { N_( "Indonesian" ),       "",            "id", "ind", "ind" },
-    { N_( "Inupiaq" ),          "",            "ik", "ipk", "ipk" },
-    { N_( "Italian" ),          "Italiano",    "it", "ita", "ita" },
-    { N_( "Javanese" ),         "",            "jv", "jaw", "jav" },
-    { N_( "Japanese" ),         "",            "ja", "jpn", "jpn" },
-    { N_( "Kalaallisut (Greenlandic)" ), "",   "kl", "kal", "kal" },
-    { N_( "Kannada" ),          "",            "kn", "kan", "kan" },
-    { N_( "Kashmiri" ),         "",            "ks", "kas", "kas" },
-    { N_( "Kazakh" ),           "",            "kk", "kaz", "kaz" },
-    { N_( "Khmer" ),            "",            "km", "khm", "khm" },
-    { N_( "Kikuyu" ),           "",            "ki", "kik", "kik" },
-    { N_( "Kinyarwanda" ),      "",            "rw", "kin", "kin" },
-    { N_( "Kirghiz" ),          "",            "ky", "kir", "kir" },
-    { N_( "Komi" ),             "",            "kv", "kom", "kom" },
-    { N_( "Korean" ),           "",            "ko", "kor", "kor" },
-    { N_( "Kuanyama" ),         "",            "kj", "kua", "kua" },
-    { N_( "Kurdish" ),          "",            "ku", "kur", "kur" },
-    { N_( "Lao" ),              "",            "lo", "lao", "lao" },
-    { N_( "Latin" ),            "",            "la", "lat", "lat" },
-    { N_( "Latvian" ),          "",            "lv", "lav", "lav" },
-    { N_( "Lingala" ),          "",            "ln", "lin", "lin" },
-    { N_( "Lithuanian" ),       "",            "lt", "lit", "lit" },
-    { N_( "Letzeburgesch" ),    "",            "lb", "ltz", "ltz" },
-    { N_( "Macedonian" ),       "",            "mk", "mkd", "mac" },
-    { N_( "Marshall" ),         "",            "mh", "mah", "mah" },
-    { N_( "Malayalam" ),        "",            "ml", "mal", "mal" },
-    { N_( "Maori" ),            "",            "mi", "mri", "mao" },
-    { N_( "Marathi" ),          "",            "mr", "mar", "mar" },
-    { N_( "Malay" ),            "",            "ms", "msa", "may" },
-    { N_( "Malagasy" ),         "",            "mg", "mlg", "mlg" },
-    { N_( "Maltese" ),          "",            "mt", "mlt", "mlt" },
-    { N_( "Moldavian" ),        "",            "mo", "mol", "mol" },
-    { N_( "Mongolian" ),        "",            "mn", "mon", "mon" },
-    { N_( "Nauru" ),            "",            "na", "nau", "nau" },
-    { N_( "Navajo" ),           "",            "nv", "nav", "nav" },
-    { N_( "Ndebele, South" ),   "",            "nr", "nbl", "nbl" },
-    { N_( "Ndebele, North" ),   "",            "nd", "nde", "nde" },
-    { N_( "Ndonga" ),           "",            "ng", "ndo", "ndo" },
-    { N_( "Nepali" ),           "",            "ne", "nep", "nep" },
-    { N_( "Norwegian" ),        "Norsk",       "no", "nor", "nor" },
-    { N_( "Norwegian Nynorsk" ), "",           "nn", "nno", "nno" },
-    { N_( "Norwegian Bokmaal" ), "",           "nb", "nob", "nob" },
-    { N_( "Chichewa; Nyanja" ), "",            "ny", "nya", "nya" },
-    { N_( "Occitan (post 1500); Provencal" ), "", "oc", "oci", "oci" },
-    { N_( "Oriya" ),            "",            "or", "ori", "ori" },
-    { N_( "Oromo" ),            "",            "om", "orm", "orm" },
-    { N_( "On Screen Display" ),"On Screen Display", "od", "osd", "osd" },
-    { N_( "Ossetian; Ossetic" ), "",           "os", "oss", "oss" },
-    { N_( "Panjabi" ),          "",            "pa", "pan", "pan" },
-    { N_( "Persian" ),          "",            "fa", "fas", "per" },
-    { N_( "Pali" ),             "",            "pi", "pli", "pli" },
-    { N_( "Polish" ),           "",            "pl", "pol", "pol" },
-    { N_( "Portuguese" ),       "Portugues",   "pt", "por", "por" },
-    { N_( "Pushto" ),           "",            "ps", "pus", "pus" },
-    { N_( "Quechua" ),          "",            "qu", "que", "que" },
-    { N_( "Raeto-Romance" ),    "",            "rm", "roh", "roh" },
-    { N_( "Romanian" ),         "",            "ro", "ron", "rum" },
-    { N_( "Rundi" ),            "",            "rn", "run", "run" },
-    { N_( "Russian" ),          "",            "ru", "rus", "rus" },
-    { N_( "Sango" ),            "",            "sg", "sag", "sag" },
-    { N_( "Sanskrit" ),         "",            "sa", "san", "san" },
-    { N_( "Serbian" ),          "",            "sr", "srp", "scc" },
-    { N_( "Croatian" ),         "Hrvatski",    "hr", "hrv", "scr" },
-    { N_( "Sinhalese" ),        "",            "si", "sin", "sin" },
-    { N_( "Slovak" ),           "",            "sk", "slk", "slo" },
-    { N_( "Slovenian" ),        "",            "sl", "slv", "slv" },
-    { N_( "Northern Sami" ),    "",            "se", "sme", "sme" },
-    { N_( "Samoan" ),           "",            "sm", "smo", "smo" },
-    { N_( "Shona" ),            "",            "sn", "sna", "sna" },
-    { N_( "Sindhi" ),           "",            "sd", "snd", "snd" },
-    { N_( "Somali" ),           "",            "so", "som", "som" },
-    { N_( "Sotho, Southern" ),  "",            "st", "sot", "sot" },
-    { N_( "Spanish" ),          "Espanol",     "es", "spa", "spa" },
-    { N_( "Sardinian" ),        "",            "sc", "srd", "srd" },
-    { N_( "Swati" ),            "",            "ss", "ssw", "ssw" },
-    { N_( "Sundanese" ),        "",            "su", "sun", "sun" },
-    { N_( "Swahili" ),          "",            "sw", "swa", "swa" },
-    { N_( "Swedish" ),          "Svenska",     "sv", "swe", "swe" },
-    { N_( "Tahitian" ),         "",            "ty", "tah", "tah" },
-    { N_( "Tamil" ),            "",            "ta", "tam", "tam" },
-    { N_( "Tatar" ),            "",            "tt", "tat", "tat" },
-    { N_( "Telugu" ),           "",            "te", "tel", "tel" },
-    { N_( "Tajik" ),            "",            "tg", "tgk", "tgk" },
-    { N_( "Tagalog" ),          "",            "tl", "tgl", "tgl" },
-    { N_( "Thai" ),             "",            "th", "tha", "tha" },
-    { N_( "Tibetan" ),          "",            "bo", "bod", "tib" },
-    { N_( "Tigrinya" ),         "",            "ti", "tir", "tir" },
-    { N_( "Tonga (Tonga Islands)" ), "",       "to", "ton", "ton" },
-    { N_( "Tswana" ),           "",            "tn", "tsn", "tsn" },
-    { N_( "Tsonga" ),           "",            "ts", "tso", "tso" },
-    { N_( "Turkish" ),          "",            "tr", "tur", "tur" },
-    { N_( "Turkmen" ),          "",            "tk", "tuk", "tuk" },
-    { N_( "Twi" ),              "",            "tw", "twi", "twi" },
-    { N_( "Uighur" ),           "",            "ug", "uig", "uig" },
-    { N_( "Ukrainian" ),        "",            "uk", "ukr", "ukr" },
-    { N_( "Urdu" ),             "",            "ur", "urd", "urd" },
-    { N_( "Uzbek" ),            "",            "uz", "uzb", "uzb" },
-    { N_( "Vietnamese" ),       "",            "vi", "vie", "vie" },
-    { N_( "Volapuk" ),          "",            "vo", "vol", "vol" },
-    { N_( "Welsh" ),            "",            "cy", "cym", "wel" },
-    { N_( "Wolof" ),            "",            "wo", "wol", "wol" },
-    { N_( "Xhosa" ),            "",            "xh", "xho", "xho" },
-    { N_( "Yiddish" ),          "",            "yi", "yid", "yid" },
-    { N_( "Yoruba" ),           "",            "yo", "yor", "yor" },
-    { N_( "Zhuang" ),           "",            "za", "zha", "zha" },
-    { N_( "Zulu" ),             "",            "zu", "zul", "zul" },
-    { NULL, NULL, NULL, NULL, NULL }
-};
-
diff --git a/src/misc/iso_lang.c b/src/misc/iso_lang.c
deleted file mode 100644 (file)
index 30c011b..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/*****************************************************************************
- * iso_lang.c: function to decode language code (in dvd or a52 for instance).
- *****************************************************************************
- * Copyright (C) 1998-2004 the VideoLAN team
- * $Id$
- *
- * Author: Stéphane Borel <stef@via.ecp.fr>
- *         Arnaud de Bossoreille de Ribou <bozo@via.ecp.fr>
- *
- * 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
- *****************************************************************************/
-
-/*****************************************************************************
- * Preamble
- *****************************************************************************/
-#include <stdio.h>
-
-#include <vlc/vlc.h>
-
-#include "iso_lang.h"
-
-/*****************************************************************************
- * Local tables
- *****************************************************************************/
-#include "iso-639_def.h"
-
-static const iso639_lang_t unknown_language =
-    { "Unknown", "Unknown", "??", "???", "???" };
-
-/*****************************************************************************
- * DecodeLanguage: gives the long language name from the two-letter
- *                 ISO-639 code
- *****************************************************************************/
-const char * DecodeLanguage( uint16_t i_code )
-{
-    const iso639_lang_t * p_lang;
-    uint8_t psz_code[3];
-
-    psz_code[0] = i_code >> 8;
-    psz_code[1] = i_code & 0xff;
-    psz_code[2] = '\0';
-
-    for( p_lang = p_languages; p_lang->psz_eng_name; p_lang++ )
-    {
-        if( !memcmp( p_lang->psz_iso639_1, psz_code, 2 ) )
-        {
-# if 0
-            if( *p_lang->psz_native_name )
-            {
-                return p_lang->psz_native_name;
-            }
-#endif
-
-            return _( p_lang->psz_eng_name );
-        }
-    }
-
-    return _( "Unknown" );
-}
-
-const iso639_lang_t * GetLang_1( const char * psz_code )
-{
-    const iso639_lang_t *p_lang;
-
-    for( p_lang = p_languages; p_lang->psz_eng_name; p_lang++ )
-        if( !strncmp( p_lang->psz_iso639_1, psz_code, 2 ) )
-            return p_lang;
-
-    return &unknown_language;
-}
-
-const iso639_lang_t * GetLang_2T( const char * psz_code )
-{
-    const iso639_lang_t *p_lang;
-
-    for( p_lang = p_languages; p_lang->psz_eng_name; p_lang++ )
-        if( !strncmp( p_lang->psz_iso639_2T, psz_code, 3 ) )
-            return p_lang;
-
-    return &unknown_language;
-}
-
-const iso639_lang_t * GetLang_2B( const char * psz_code )
-{
-    const iso639_lang_t *p_lang;
-
-    for( p_lang = p_languages; p_lang->psz_eng_name; p_lang++ )
-        if( !strncmp( p_lang->psz_iso639_2B, psz_code, 3 ) )
-            return p_lang;
-
-    return &unknown_language;
-}
-
diff --git a/src/misc/strings.c b/src/misc/strings.c
deleted file mode 100644 (file)
index f6e1423..0000000
+++ /dev/null
@@ -1,648 +0,0 @@
-/*****************************************************************************
- * strings.c: String related functions
- *****************************************************************************
- * Copyright (C) 2006 the VideoLAN team
- * $Id$
- *
- * Authors: Antoine Cellerier <dionoea at videolan dot org>
- *          Daniel Stranger <vlc at schmaller dot de>
- *          Rémi Denis-Courmont <rem # videolan org>
- *
- * 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
- *****************************************************************************/
-
-/*****************************************************************************
- * Preamble
- *****************************************************************************/
-#include <vlc/vlc.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <assert.h>
-
-/* Needed by str_format_time */
-#include <time.h>
-
-/* Needed by str_format_meta */
-#include "vlc_input.h"
-#include "vlc_meta.h"
-#include "vlc_playlist.h"
-#include <vlc/aout.h>
-
-#include "vlc_strings.h"
-#include "vlc_url.h"
-#include "charset.h"
-
-/**
- * Unescape URI encoded string
- * \return decoded duplicated string
- */
-char *unescape_URI_duplicate( const char *psz )
-{
-    char *psz_dup = strdup( psz );
-    unescape_URI( psz_dup );
-    return psz_dup;
-}
-
-/**
- * Unescape URI encoded string in place
- * \return nothing
- */
-void unescape_URI( char *psz )
-{
-    unsigned char *in = (unsigned char *)psz, *out = in, c;
-
-    while( ( c = *in++ ) != '\0' )
-    {
-        switch( c )
-        {
-            case '%':
-            {
-                char val[5], *pval = val;
-                unsigned long cp;
-
-                switch( c = *in++ )
-                {
-                    case '\0':
-                        return;
-
-                    case 'u':
-                    case 'U':
-                        if( ( *pval++ = *in++ ) == '\0' )
-                            return;
-                        if( ( *pval++ = *in++ ) == '\0' )
-                            return;
-                        c = *in++;
-
-                    default:
-                        *pval++ = c;
-                        if( ( *pval++ = *in++ ) == '\0' )
-                            return;
-                        *pval = '\0';
-                }
-
-                cp = strtoul( val, NULL, 0x10 );
-                if( cp < 0x80 )
-                    *out++ = cp;
-                else
-                if( cp < 0x800 )
-                {
-                    *out++ = (( cp >>  6)         | 0xc0);
-                    *out++ = (( cp        & 0x3f) | 0x80);
-                }
-                else
-                {
-                    assert( cp < 0x10000 );
-                    *out++ = (( cp >> 12)         | 0xe0);
-                    *out++ = (((cp >>  6) & 0x3f) | 0x80);
-                    *out++ = (( cp        & 0x3f) | 0x80);
-                }
-                break;
-            }
-
-            /* + is not a special case - it means plus, not space. */
-
-            default:
-                /* Inserting non-ASCII or non-printable characters is unsafe,
-                 * and no sane browser will send these unencoded */
-                if( ( c < 32 ) || ( c > 127 ) )
-                    *out++ = '?';
-                else
-                    *out++ = c;
-        }
-    }
-    *out = '\0';
-}
-
-/**
- * Decode encoded URI string
- * \return decoded duplicated string
- */
-char *decode_URI_duplicate( const char *psz )
-{
-    char *psz_dup = strdup( psz );
-    decode_URI( psz_dup );
-    return psz_dup;
-}
-
-/**
- * Decode encoded URI string in place
- * \return nothing
- */
-void decode_URI( char *psz )
-{
-    unsigned char *in = (unsigned char *)psz, *out = in, c;
-
-    while( ( c = *in++ ) != '\0' )
-    {
-        switch( c )
-        {
-            case '%':
-            {
-                char hex[3];
-
-                if( ( ( hex[0] = *in++ ) == 0 )
-                 || ( ( hex[1] = *in++ ) == 0 ) )
-                    return;
-
-                hex[2] = '\0';
-                *out++ = (unsigned char)strtoul( hex, NULL, 0x10 );
-                break;
-            }
-
-            case '+':
-                *out++ = ' ';
-                break;
-
-            default:
-                /* Inserting non-ASCII or non-printable characters is unsafe,
-                 * and no sane browser will send these unencoded */
-                if( ( c < 32 ) || ( c > 127 ) )
-                    *out++ = '?';
-                else
-                    *out++ = c;
-        }
-    }
-    *out = '\0';
-    EnsureUTF8( psz );
-}
-
-static inline int isurlsafe( int c )
-{
-    return ( (unsigned char)( c - 'a' ) < 26 )
-            || ( (unsigned char)( c - 'A' ) < 26 )
-            || ( (unsigned char)( c - '0' ) < 10 )
-        /* Hmm, we should not encode character that are allowed in URLs
-         * (even if they are not URL-safe), nor URL-safe characters.
-         * We still encode some of them because of Microsoft's crap browser.
-         */
-            || ( strchr( "-_.", c ) != NULL );
-}
-
-static inline char url_hexchar( int c )
-{
-    return ( c < 10 ) ? c + '0' : c + 'A' - 10;
-}
-
-/**
- * encode_URI_component
- * Encodes an URI component.
- *
- * @param psz_url nul-terminated UTF-8 representation of the component.
- * Obviously, you can't pass an URI containing a nul character, but you don't
- * want to do that, do you?
- *
- * @return encoded string (must be free()'d)
- */
-char *encode_URI_component( const char *psz_url )
-{
-    char psz_enc[3 * strlen( psz_url ) + 1], *out = psz_enc;
-    const uint8_t *in;
-
-    for( in = (const uint8_t *)psz_url; *in; in++ )
-    {
-        uint8_t c = *in;
-
-        if( isurlsafe( c ) )
-            *out++ = (char)c;
-        else
-        if ( c == ' ')
-            *out++ = '+';
-        else
-        {
-            *out++ = '%';
-            *out++ = url_hexchar( c >> 4 );
-            *out++ = url_hexchar( c & 0xf );
-        }
-    }
-    *out++ = '\0';
-
-    return strdup( psz_enc );
-}
-
-/**
- * Converts "&lt;", "&gt;" and "&amp;" to "<", ">" and "&"
- * \param string to convert
- */
-void resolve_xml_special_chars( char *psz_value )
-{
-    char *p_pos = psz_value;
-
-    while ( *psz_value )
-    {
-        if( !strncmp( psz_value, "&lt;", 4 ) )
-        {
-            *p_pos = '<';
-            psz_value += 4;
-        }
-        else if( !strncmp( psz_value, "&gt;", 4 ) )
-        {
-            *p_pos = '>';
-            psz_value += 4;
-        }
-        else if( !strncmp( psz_value, "&amp;", 5 ) )
-        {
-            *p_pos = '&';
-            psz_value += 5;
-        }
-        else if( !strncmp( psz_value, "&quot;", 6 ) )
-        {
-            *p_pos = '\"';
-            psz_value += 6;
-        }
-        else if( !strncmp( psz_value, "&#039;", 6 ) )
-        {
-            *p_pos = '\'';
-            psz_value += 6;
-        }
-        else
-        {
-            *p_pos = *psz_value;
-            psz_value++;
-        }
-
-        p_pos++;
-    }
-
-    *p_pos = '\0';
-}
-
-/**
- * Converts '<', '>', '\"', '\'' and '&' to their html entities
- * \param psz_content simple element content that is to be converted
- */
-char *convert_xml_special_chars( const char *psz_content )
-{
-    char *psz_temp = malloc( 6 * strlen( psz_content ) + 1 );
-    const char *p_from = psz_content;
-    char *p_to   = psz_temp;
-
-    while ( *p_from )
-    {
-        if ( *p_from == '<' )
-        {
-            strcpy( p_to, "&lt;" );
-            p_to += 4;
-        }
-        else if ( *p_from == '>' )
-        {
-            strcpy( p_to, "&gt;" );
-            p_to += 4;
-        }
-        else if ( *p_from == '&' )
-        {
-            strcpy( p_to, "&amp;" );
-            p_to += 5;
-        }
-        else if( *p_from == '\"' )
-        {
-            strcpy( p_to, "&quot;" );
-            p_to += 6;
-        }
-        else if( *p_from == '\'' )
-        {
-            strcpy( p_to, "&#039;" );
-            p_to += 6;
-        }
-        else
-        {
-            *p_to = *p_from;
-            p_to++;
-        }
-        p_from++;
-    }
-    *p_to = '\0';
-
-    return psz_temp;
-}
-
-/****************************************************************************
- * String formating functions
- ****************************************************************************/
-char *str_format_time(char *tformat )
-{
-    char buffer[255];
-    time_t curtime;
-#if defined(HAVE_LOCALTIME_R)
-    struct tm loctime;
-#else
-    struct tm *loctime;
-#endif
-
-    /* Get the current time.  */
-    curtime = time( NULL );
-
-    /* Convert it to local time representation.  */
-#if defined(HAVE_LOCALTIME_R)
-    localtime_r( &curtime, &loctime );
-    strftime( buffer, 255, tformat, &loctime );
-#else
-    loctime = localtime( &curtime );
-    strftime( buffer, 255, tformat, loctime );
-#endif
-    return strdup( buffer );
-}
-
-#define INSERT_STRING( check, string )                              \
-                    if( check && string )                           \
-                    {                                               \
-                        int len = strlen( string );                 \
-                        dst = realloc( dst,                         \
-                                       i_size = i_size + len + 1 ); \
-                        strncpy( d, string, len+1 );                \
-                        d += len;                                   \
-                    }                                               \
-                    else                                            \
-                    {                                               \
-                        *d = '-';                                   \
-                        d++;                                        \
-                    }
-char *__str_format_meta( vlc_object_t *p_object, char *string )
-{
-    char *s = string;
-    char *dst = malloc( 1000 );
-    char *d = dst;
-    int b_is_format = 0;
-    char buf[10];
-    int i_size = strlen( string );
-
-    playlist_t *p_playlist = pl_Yield( p_object );
-    input_thread_t *p_input = p_playlist->p_input;
-    input_item_t *p_item = NULL;
-    pl_Release( p_object );
-    if( p_input )
-    {
-        vlc_object_yield( p_input );
-        p_item = p_input->input.p_item;
-        if( p_item )
-            vlc_mutex_lock( &p_item->lock );
-    }
-
-    sprintf( dst, string );
-
-    while( *s )
-    {
-        if( b_is_format )
-        {
-            switch( *s )
-            {
-                case 'a':
-                    INSERT_STRING( p_item && p_item->p_meta,
-                                   p_item->p_meta->psz_artist );
-                    break;
-                case 'b':
-                    INSERT_STRING( p_item && p_item->p_meta,
-                                   p_item->p_meta->psz_album );
-                    break;
-                case 'c':
-                    INSERT_STRING( p_item && p_item->p_meta,
-                                   p_item->p_meta->psz_copyright );
-                    break;
-                case 'd':
-                    INSERT_STRING( p_item && p_item->p_meta,
-                                   p_item->p_meta->psz_description );
-                    break;
-                case 'e':
-                    INSERT_STRING( p_item && p_item->p_meta,
-                                   p_item->p_meta->psz_encodedby );
-                    break;
-                case 'g':
-                    INSERT_STRING( p_item && p_item->p_meta,
-                                   p_item->p_meta->psz_genre );
-                    break;
-                case 'l':
-                    INSERT_STRING( p_item && p_item->p_meta,
-                                   p_item->p_meta->psz_language );
-                    break;
-                case 'n':
-                    INSERT_STRING( p_item && p_item->p_meta,
-                                   p_item->p_meta->psz_tracknum );
-                    break;
-                case 'p':
-                    INSERT_STRING( p_item && p_item->p_meta,
-                                   p_item->p_meta->psz_nowplaying );
-                    break;
-                case 'r':
-                    INSERT_STRING( p_item && p_item->p_meta,
-                                   p_item->p_meta->psz_rating );
-                    break;
-                case 's':
-                {
-                    char *lang;
-                    if( p_input )
-                    {
-                        lang = var_GetString( p_input, "sub-language" );
-                    }
-                    else
-                    {
-                        lang = strdup( "-" );
-                    }
-                    INSERT_STRING( 1, lang );
-                    free( lang );
-                    break;
-                }
-                case 't':
-                    INSERT_STRING( p_item && p_item->p_meta,
-                                   p_item->p_meta->psz_title );
-                    break;
-                case 'u':
-                    INSERT_STRING( p_item && p_item->p_meta,
-                                   p_item->p_meta->psz_url );
-                    break;
-                case 'A':
-                    INSERT_STRING( p_item && p_item->p_meta,
-                                   p_item->p_meta->psz_date );
-                    break;
-                case 'B':
-                    if( p_input )
-                    {
-                        snprintf( buf, 10, "%d",
-                                  var_GetInteger( p_input, "bit-rate" )/1000 );
-                    }
-                    else
-                    {
-                        sprintf( buf, "-" );
-                    }
-                    INSERT_STRING( 1, buf );
-                    break;
-                case 'C':
-                    if( p_input )
-                    {
-                        snprintf( buf, 10, "%d",
-                                  var_GetInteger( p_input, "chapter" ) );
-                    }
-                    else
-                    {
-                        sprintf( buf, "-" );
-                    }
-                    INSERT_STRING( 1, buf );
-                    break;
-                case 'D':
-                    if( p_item )
-                    {
-                        sprintf( buf, "%02d:%02d:%02d",
-                                 (int)(p_item->i_duration/(3600000000)),
-                                 (int)((p_item->i_duration/(60000000))%60),
-                                 (int)((p_item->i_duration/1000000)%60) );
-                    }
-                    else
-                    {
-                        sprintf( buf, "--:--:--" );
-                    }
-                    INSERT_STRING( 1, buf );
-                    break;
-                case 'F':
-                    INSERT_STRING( p_item, p_item->psz_uri );
-                    break;
-                case 'I':
-                    if( p_input )
-                    {
-                        snprintf( buf, 10, "%d",
-                                  var_GetInteger( p_input, "title" ) );
-                    }
-                    else
-                    {
-                        sprintf( buf, "-" );
-                    }
-                    INSERT_STRING( 1, buf );
-                    break;
-                case 'L':
-                    if( p_item && p_input )
-                    {
-                        sprintf( buf, "%02d:%02d:%02d",
-                     (int)((p_item->i_duration-p_input->i_time)/(3600000000)),
-                     (int)(((p_item->i_duration-p_input->i_time)/(60000000))%60),
-                     (int)(((p_item->i_duration-p_input->i_time)/1000000)%60) );
-                    }
-                    else
-                    {
-                        sprintf( buf, "--:--:--" );
-                    }
-                    INSERT_STRING( 1, buf );
-                    break;
-                case 'N':
-                    INSERT_STRING( p_item, p_item->psz_name );
-                    break;
-                case 'O':
-                {
-                    char *lang;
-                    if( p_input )
-                    {
-                        lang = var_GetString( p_input, "audio-language" );
-                    }
-                    else
-                    {
-                        lang = strdup( "-" );
-                    }
-                    INSERT_STRING( 1, lang );
-                    free( lang );
-                    break;
-                }
-                case 'P':
-                    if( p_input )
-                    {
-                        snprintf( buf, 10, "%2.1lf",
-                                  var_GetFloat( p_input, "position" ) * 100. );
-                    }
-                    else
-                    {
-                        sprintf( buf, "--.-%%" );
-                    }
-                    INSERT_STRING( 1, buf );
-                    break;
-                case 'R':
-                    if( p_input )
-                    {
-                        int r = var_GetInteger( p_input, "rate" );
-                        snprintf( buf, 10, "%d.%d", r/1000, r%1000 );
-                    }
-                    else
-                    {
-                        sprintf( buf, "-" );
-                    }
-                    INSERT_STRING( 1, buf );
-                    break;
-                case 'S':
-                    if( p_input )
-                    {
-                        int r = var_GetInteger( p_input, "sample-rate" );
-                        snprintf( buf, 10, "%d.%d", r/1000, (r/100)%10 );
-                    }
-                    else
-                    {
-                        sprintf( buf, "-" );
-                    }
-                    INSERT_STRING( 1, buf );
-                    break;
-                case 'T':
-                    if( p_input )
-                    {
-                        sprintf( buf, "%02d:%02d:%02d",
-                                 (int)(p_input->i_time/(3600000000)),
-                                 (int)((p_input->i_time/(60000000))%60),
-                                 (int)((p_input->i_time/1000000)%60) );
-                    }
-                    else
-                    {
-                        sprintf( buf, "--:--:--" );
-                    }
-                    INSERT_STRING( 1, buf );
-                    break;
-                case 'U':
-                    INSERT_STRING( p_item && p_item->p_meta,
-                                   p_item->p_meta->psz_publisher );
-                    break;
-                case 'V':
-                {
-                    audio_volume_t volume;
-                    aout_VolumeGet( p_object, &volume );
-                    snprintf( buf, 10, "%d", volume );
-                    INSERT_STRING( 1, buf );
-                    break;
-                }
-                case '_':
-                    *d = '\n';
-                    d++;
-                    break;
-
-                default:
-                    *d = *s;
-                    d++;
-                    break;
-            }
-            b_is_format = 0;
-        }
-        else if( *s == '$' )
-        {
-            b_is_format = 1;
-        }
-        else
-        {
-            *d = *s;
-            d++;
-        }
-        s++;
-    }
-    *d = '\0';
-
-    if( p_input )
-    {
-        vlc_object_release( p_input );
-        if( p_item )
-            vlc_mutex_unlock( &p_item->lock );
-    }
-
-    return dst;
-}
diff --git a/src/misc/unicode.c b/src/misc/unicode.c
deleted file mode 100644 (file)
index 03b167e..0000000
+++ /dev/null
@@ -1,777 +0,0 @@
-/*****************************************************************************
- * unicode.c: Unicode <-> locale functions
- *****************************************************************************
- * Copyright (C) 2005-2006 the VideoLAN team
- * Copyright © 2005-2006 Rémi Denis-Courmont
- * $Id$
- *
- * Authors: Rémi Denis-Courmont <rem # videolan.org>
- *
- * 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
- *****************************************************************************/
-
-/*****************************************************************************
- * Preamble
- *****************************************************************************/
-#include <vlc/vlc.h>
-#include "charset.h"
-
-#include <assert.h>
-
-#include <stdio.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <sys/types.h>
-#ifdef HAVE_DIRENT_H
-#  include <dirent.h>
-#endif
-#ifdef UNDER_CE
-#  include <tchar.h>
-#endif
-#ifdef HAVE_SYS_STAT_H
-# include <sys/stat.h>
-#endif
-#ifdef HAVE_FCNTL_H
-# include <fcntl.h>
-#endif
-#ifdef WIN32
-# include <io.h>
-#else
-# include <unistd.h>
-#endif
-
-#ifndef HAVE_LSTAT
-# define lstat( a, b ) stat(a, b)
-#endif
-
-#ifdef __APPLE__
-/* Define this if the OS always use UTF-8 internally */
-# define ASSUME_UTF8 1
-#endif
-
-#ifndef ASSUME_UTF8
-# if defined (HAVE_ICONV)
-/* libiconv is more powerful than Win32 API (it has translit) */
-#  define USE_ICONV 1
-# elif defined (WIN32) || defined (UNDER_CE)
-#  define USE_MB2MB 1
-# else
-#  error No UTF8 charset conversion implemented on this platform!
-# endif
-#endif
-
-typedef struct locale_data_t
-{
-#if defined (USE_ICONV)
-    vlc_iconv_t hd;
-    vlc_mutex_t lock;
-#elif defined (USE_MB2MB)
-    UINT fromCP;
-    UINT toCP;
-#endif
-} locale_data_t;
-
-static locale_data_t from_locale, to_locale;
-
-
-void LocaleInit( vlc_object_t *p_this )
-{
-#if defined USE_ICONV
-    char *psz_charset;
-
-    if( vlc_current_charset( &psz_charset ) )
-        /* UTF-8 */
-        from_locale.hd = to_locale.hd = (vlc_iconv_t)(-1);
-    else
-    {
-        /* not UTF-8 */
-        char psz_buf[strlen( psz_charset ) + sizeof( "//translit" )];
-        const char *psz_conv;
-
-        /*
-         * Still allow non-ASCII characters when the locale is not set.
-         * Western Europeans are being favored for historical reasons.
-         */
-        if( strcmp( psz_charset, "ASCII" ) )
-        {
-            sprintf( psz_buf, "%s//translit", psz_charset );
-            psz_conv = psz_buf;
-        }
-        else
-            psz_conv = "ISO-8859-1//translit";
-
-        vlc_mutex_init( p_this, &from_locale.lock );
-        vlc_mutex_init( p_this, &to_locale.lock );
-        from_locale.hd = vlc_iconv_open( "UTF-8", psz_conv );
-        to_locale.hd = vlc_iconv_open( psz_conv, "UTF-8" );
-    }
-
-    free( psz_charset );
-
-    assert( (from_locale.hd == (vlc_iconv_t)(-1))
-            == (to_locale.hd == (vlc_iconv_t)(-1)) );
-
-#elif defined (USE_MB2MB)
-    to_locale.toCP = from_locale.fromCP = CP_ACP;
-    from_locale.toCP = to_locale.fromCP = CP_UTF8;
-#else
-    (void)p_this;
-#endif
-}
-
-void LocaleDeinit( void )
-{
-#ifdef USE_ICONV
-    if( to_locale.hd != (vlc_iconv_t)(-1) )
-    {
-        vlc_iconv_close( to_locale.hd );
-        vlc_mutex_destroy( &to_locale.lock );
-    }
-
-    if( from_locale.hd != (vlc_iconv_t)(-1) )
-    {
-        vlc_iconv_close( from_locale.hd );
-        vlc_mutex_destroy( &from_locale.lock );
-    }
-#endif
-}
-
-static char *locale_fast (const char *string, locale_data_t *p)
-{
-#if defined (USE_ICONV)
-    vlc_iconv_t hd = p->hd;
-
-    if (hd == (vlc_iconv_t)(-1))
-        return (char *)string;
-
-    const char *iptr = string;
-    size_t inb = strlen (string);
-    size_t outb = inb * 6 + 1;
-    char output[outb], *optr = output;
-
-    if (string == NULL)
-        return NULL;
-
-    vlc_mutex_lock (&p->lock);
-    vlc_iconv (hd, NULL, NULL, NULL, NULL);
-
-    while (vlc_iconv (hd, &iptr, &inb, &optr, &outb) == (size_t)(-1))
-    {
-        *optr++ = '?';
-        outb--;
-        iptr++;
-        inb--;
-        vlc_iconv (hd, NULL, NULL, NULL, NULL);
-    }
-    vlc_mutex_unlock (&p->lock);
-    *optr = '\0';
-
-    assert (inb == 0);
-    assert (*iptr == '\0');
-    assert (*optr == '\0');
-    assert (strlen (output) == (size_t)(optr - output));
-    return strdup (output);
-#elif defined (USE_MB2MB)
-    char *out;
-    wchar_t *wide;
-    int len;
-
-    if (string == NULL)
-        return NULL;
-
-    len = MultiByteToWideChar (p->fromCP, 0, string, -1, NULL, 0);
-    if (len == 0)
-        return NULL;
-
-    wchar_t wide[len];
-
-    MultiByteToWideChar (p->fromCP, 0, string, -1, wide, len);
-    len = WideCharToMultiByte (p->toCP, 0, wide, -1, NULL, 0, NULL, NULL);
-    if (len == 0)
-        return NULL;
-    out = malloc (len);
-
-    WideCharToMultiByte (p->toCP, 0, wide, -1, out, len, NULL, NULL);
-    return out;
-#else
-    return (char *)string;
-#endif
-}
-
-
-static inline char *locale_dup (const char *string, locale_data_t *p)
-{
-#if defined (USE_ICONV)
-    return (p->hd == (vlc_iconv_t)(-1))
-            ? strdup (string)
-            : locale_fast (string, p);
-#elif defined (USE_MB2MB)
-    return locale_fast (string, p);
-#else
-    return strdup (string);
-#endif
-}
-
-
-void LocaleFree (const char *str)
-{
-#if defined (USE_ICONV)
-    assert ((to_locale.hd == (vlc_iconv_t)(-1))
-         == (from_locale.hd == (vlc_iconv_t)(-1)));
-
-    if( to_locale.hd != (vlc_iconv_t)(-1) )
-        free ((char *)str);
-#elif defined (USE_MB2MB)
-    free ((char *)str);
-#endif
-}
-
-
-/**
- * FromLocale: converts a locale string to UTF-8
- *
- * @param locale nul-terminated string to be converted
- *
- * @return a nul-terminated UTF-8 string, or NULL in case of error.
- * To avoid memory leak, you have to pass the result to LocaleFree()
- * when it is no longer needed.
- */
-char *FromLocale (const char *locale)
-{
-    return locale_fast (locale, &from_locale);
-}
-
-char *FromLocaleDup (const char *locale)
-{
-    return locale_dup (locale, &from_locale);
-}
-
-
-/**
- * ToLocale: converts a UTF-8 string to local system encoding.
- *
- * @param utf8 nul-terminated string to be converted
- *
- * @return a nul-terminated string, or NULL in case of error.
- * To avoid memory leak, you have to pass the result to LocaleFree()
- * when it is no longer needed.
- */
-char *ToLocale (const char *utf8)
-{
-    return locale_fast (utf8, &to_locale);
-}
-
-
-static char *ToLocaleDup (const char *utf8)
-{
-    return locale_dup (utf8, &to_locale);
-}
-
-
-/**
- * utf8_open: open() wrapper for UTF-8 filenames
- */
-int utf8_open (const char *filename, int flags, mode_t mode)
-{
-#if defined (WIN32) || defined (UNDER_CE)
-    if (GetVersion() < 0x80000000)
-    {
-        /* for Windows NT and above */
-        wchar_t wpath[MAX_PATH + 1];
-
-        if (!MultiByteToWideChar (CP_UTF8, 0, filename, -1, wpath, MAX_PATH))
-        {
-            errno = ENOENT;
-            return -1;
-        }
-        wpath[MAX_PATH] = L'\0';
-
-        /*
-         * open() cannot open files with non-“ANSI” characters on Windows.
-         * We use _wopen() instead. Same thing for mkdir() and stat().
-         */
-        return _wopen (wpath, flags, mode);
-    }
-#endif
-    const char *local_name = ToLocale (filename);
-
-    if (local_name == NULL)
-    {
-        errno = ENOENT;
-        return -1;
-    }
-
-    int fd = open (local_name, flags, mode);
-    LocaleFree (local_name);
-    return fd;
-}
-
-/**
- * utf8_fopen: fopen() wrapper for UTF-8 filenames
- */
-FILE *utf8_fopen (const char *filename, const char *mode)
-{
-    int rwflags = 0, oflags = 0;
-    vlc_bool_t append = VLC_FALSE;
-
-    for (const char *ptr = mode; *ptr; ptr++)
-    {
-        switch (*ptr)
-        {
-            case 'r':
-                rwflags = O_RDONLY;
-                break;
-
-            case 'a':
-                rwflags = O_WRONLY;
-                oflags |= O_CREAT;
-                append = VLC_TRUE;
-                break;
-
-            case 'w':
-                rwflags = O_WRONLY;
-                oflags |= O_CREAT | O_TRUNC;
-                break;
-
-            case '+':
-                rwflags = O_RDWR;
-                break;
-        }
-    }
-
-    int fd = utf8_open (filename, rwflags | oflags, 0666);
-    if (fd == -1)
-        return NULL;
-
-    if (append && (lseek (fd, 0, SEEK_END) == -1))
-    {
-        close (fd);
-        return NULL;
-    }
-
-    FILE *stream = fdopen (fd, mode);
-    if (stream == NULL)
-        close (fd);
-
-    return stream;
-}
-
-/**
- * utf8_mkdir: Calls mkdir() after conversion of file name to OS locale
- */
-int utf8_mkdir( const char *dirname )
-{
-#if defined (UNDER_CE) || defined (WIN32)
-    wchar_t wname[MAX_PATH + 1];
-    char mod[MAX_PATH + 1];
-    int i;
-
-    /* Convert '/' into '\' */
-    for( i = 0; *dirname; i++ )
-    {
-        if( i == MAX_PATH )
-            return -1; /* overflow */
-
-        if( *dirname == '/' )
-            mod[i] = '\\';
-        else
-            mod[i] = *dirname;
-        dirname++;
-
-    }
-    mod[i] = 0;
-
-    if( MultiByteToWideChar( CP_UTF8, 0, mod, -1, wname, MAX_PATH ) == 0 )
-    {
-        errno = ENOENT;
-        return -1;
-    }
-    wname[MAX_PATH] = L'\0';
-
-    if( CreateDirectoryW( wname, NULL ) == 0 )
-    {
-        if( GetLastError( ) == ERROR_ALREADY_EXISTS )
-            errno = EEXIST;
-        else
-            errno = ENOENT;
-        return -1;
-    }
-    return 0;
-#else
-    char *locname = ToLocale( dirname );
-    int res;
-
-    if( locname == NULL )
-    {
-        errno = ENOENT;
-        return -1;
-    }
-    res = mkdir( locname, 0755 );
-
-    LocaleFree( locname );
-    return res;
-#endif
-}
-
-
-DIR *utf8_opendir( const char *dirname )
-{
-    /* TODO: support for WinNT non-ACP filenames */
-    const char *local_name = ToLocale( dirname );
-
-    if( local_name != NULL )
-    {
-        DIR *dir = vlc_opendir_wrapper( local_name );
-        LocaleFree( local_name );
-        return dir;
-    }
-    else
-        errno = ENOENT;
-    return NULL;
-}
-
-
-char *utf8_readdir( void *dir )
-{
-    struct dirent *ent;
-
-    ent = vlc_readdir_wrapper( (DIR *)dir );
-    if( ent == NULL )
-        return NULL;
-
-    return vlc_fix_readdir( ent->d_name );
-}
-
-static int dummy_select( const char *str )
-{
-    (void)str;
-    return 1;
-}
-
-int utf8_scandir( const char *dirname, char ***namelist,
-                  int (*select)( const char * ),
-                  int (*compar)( const char **, const char ** ) )
-{
-    DIR *dir = utf8_opendir( dirname );
-
-    if( select == NULL )
-        select = dummy_select;
-
-    if( dir == NULL )
-        return -1;
-    else
-    {
-        char **tab = NULL;
-        char *entry;
-        unsigned num = 0;
-
-        while( ( entry = utf8_readdir( dir ) ) != NULL )
-        {
-            char **newtab;
-
-            if( !select( entry ) )
-            {
-                free( entry );
-                continue;
-            }
-
-            newtab = realloc( tab, sizeof( char * ) * (num + 1) );
-            if( newtab == NULL )
-            {
-                free( entry );
-                goto error;
-            }
-            tab = newtab;
-            tab[num++] = entry;
-        }
-        vlc_closedir_wrapper( dir );
-
-        if( compar != NULL )
-            qsort( tab, num, sizeof( tab[0] ),
-                   (int (*)( const void *, const void *))compar );
-
-        *namelist = tab;
-        return num;
-
-    error:{
-        unsigned i;
-
-        for( i = 0; i < num; i++ )
-            free( tab[i] );
-        if( tab != NULL )
-            free( tab );
-        return -1;}
-    }
-}
-
-
-static int utf8_statEx( const char *filename, struct stat *buf,
-                        vlc_bool_t deref )
-{
-#if defined (WIN32) || defined (UNDER_CE)
-    /* retrieve Windows OS version */
-    if( GetVersion() < 0x80000000 )
-    {
-        /* for Windows NT and above */
-        wchar_t wpath[MAX_PATH + 1];
-
-        if( !MultiByteToWideChar( CP_UTF8, 0, filename, -1, wpath, MAX_PATH ) )
-        {
-            errno = ENOENT;
-            return -1;
-        }
-        wpath[MAX_PATH] = L'\0';
-
-        return _wstati64( wpath, buf );
-    }
-#endif
-#ifdef HAVE_SYS_STAT_H
-    const char *local_name = ToLocale( filename );
-
-    if( local_name != NULL )
-    {
-        int res = deref ? stat( local_name, buf )
-                       : lstat( local_name, buf );
-        LocaleFree( local_name );
-        return res;
-    }
-    errno = ENOENT;
-#endif
-    return -1;
-}
-
-
-int utf8_stat( const char *filename, struct stat *buf)
-{
-    return utf8_statEx( filename, buf, VLC_TRUE );
-}
-
-int utf8_lstat( const char *filename, struct stat *buf)
-{
-    return utf8_statEx( filename, buf, VLC_FALSE );
-}
-
-/**
- * utf8_*printf: *printf with conversion from UTF-8 to local encoding
- */
-static int utf8_vasprintf( char **str, const char *fmt, va_list ap )
-{
-    char *utf8;
-    int res = vasprintf( &utf8, fmt, ap );
-    if( res == -1 )
-        return -1;
-
-    *str = ToLocaleDup( utf8 );
-    free( utf8 );
-    return res;
-}
-
-int utf8_vfprintf( FILE *stream, const char *fmt, va_list ap )
-{
-    char *str;
-    int res = utf8_vasprintf( &str, fmt, ap );
-    if( res == -1 )
-        return -1;
-
-    fputs( str, stream );
-    free( str );
-    return res;
-}
-
-int utf8_fprintf( FILE *stream, const char *fmt, ... )
-{
-    va_list ap;
-    int res;
-
-    va_start( ap, fmt );
-    res = utf8_vfprintf( stream, fmt, ap );
-    va_end( ap );
-    return res;
-}
-
-
-static char *CheckUTF8( char *str, char rep )
-#define isutf8cont( c ) (((c) >= 0x80) && ((c) <= 0xBF)) 
-{
-    unsigned char *ptr, c;
-
-    assert (str != NULL);
-
-    ptr = (unsigned char *)str;
-    while( (c = *ptr) != '\0' )
-    {
-        /* US-ASCII, 1 byte */
-        if( c <= 0x7F )
-            ptr++; /* OK */
-        else
-        /* 2 bytes */
-        if( ( c >= 0xC2 ) && ( c <= 0xDF ) )
-        {
-            c = ptr[1];
-            if( isutf8cont( c ) )
-                ptr += 2; /* OK */
-            else
-                goto error;
-        }
-        else
-        /* 3 bytes */
-        if( c == 0xE0 )
-        {
-            c = ptr[1];
-            if( ( c >= 0xA0 ) && ( c <= 0xBF ) )
-            {
-                c = ptr[2];
-                if( isutf8cont( c ) )
-                    ptr += 3; /* OK */
-                else
-                    goto error;
-            }
-            else
-                goto error;
-        }
-        else
-        if( ( ( c >= 0xE1 ) && ( c <= 0xEC ) ) || ( c == 0xEC )
-         || ( c == 0xEE ) || ( c == 0xEF ) )
-        {
-            c = ptr[1];
-            if( isutf8cont( c ) )
-            {
-                c = ptr[2];
-                if( isutf8cont( c ) )
-                    ptr += 3; /* OK */
-                else
-                    goto error;
-            }
-            else
-                goto error;
-        }
-        else
-        if( c == 0xED )
-        {
-            c = ptr[1];
-            if( ( c >= 0x80 ) && ( c <= 0x9F ) )
-            {
-                c = ptr[2];
-                if( isutf8cont( c ) )
-                    ptr += 3; /* OK */
-                else
-                    goto error;
-            }
-            else
-                goto error;
-        }
-        else
-        /* 4 bytes */
-        if( c == 0xF0 )
-        {
-            c = ptr[1];
-            if( ( c >= 0x90 ) && ( c <= 0xBF ) )
-            {
-                c = ptr[2];
-                if( isutf8cont( c ) )
-                {
-                    c = ptr[3];
-                    if( isutf8cont( c ) )
-                        ptr += 4; /* OK */
-                    else
-                        goto error;
-                }
-                else
-                    goto error;
-            }
-            else
-                goto error;
-        }
-        else
-        if( ( c >= 0xF1 ) && ( c <= 0xF3 ) )
-        {
-            c = ptr[1];
-            if( isutf8cont( c ) )
-            {
-                c = ptr[2];
-                if( isutf8cont( c ) )
-                {
-                    c = ptr[3];
-                    if( isutf8cont( c ) )
-                        ptr += 4; /* OK */
-                    goto error;
-                }
-                else
-                    goto error;
-            }
-            else
-                goto error;
-        }
-        else
-        if( c == 0xF4 )
-        {
-            c = ptr[1];
-            if( ( c >= 0x80 ) && ( c <= 0x8F ) )
-            {
-                c = ptr[2];
-                if( isutf8cont( c ) )
-                {
-                    c = ptr[3];
-                    if( isutf8cont( c ) )
-                        ptr += 4; /* OK */
-                    else
-                        goto error;
-                }
-                else
-                    goto error;
-            }
-            else
-                goto error;
-        }
-        else
-            goto error;
-
-        continue;
-
-error:
-        if( rep == 0 )
-            return NULL;
-        *ptr++ = '?';
-        str = NULL;
-    }
-
-    return str;
-}
-
-/**
- * EnsureUTF8: replaces invalid/overlong UTF-8 sequences with question marks
- * Note that it is not possible to convert from Latin-1 to UTF-8 on the fly,
- * so we don't try that, even though it would be less disruptive.
- *
- * @return str if it was valid UTF-8, NULL if not.
- */
-char *EnsureUTF8( char *str )
-{
-    return CheckUTF8( str, '?' );
-}
-
-
-/**
- * IsUTF8: checks whether a string is a valid UTF-8 byte sequence.
- *
- * @param str nul-terminated string to be checked
- *
- * @return str if it was valid UTF-8, NULL if not.
- */
-const char *IsUTF8( const char *str )
-{
-    return CheckUTF8( (char *)str, 0 );
-}
diff --git a/src/text/charset.c b/src/text/charset.c
new file mode 100644 (file)
index 0000000..e33d355
--- /dev/null
@@ -0,0 +1,629 @@
+/*****************************************************************************
+ * charset.c: Locale's character encoding stuff.
+ *****************************************************************************
+ * See also unicode.c for Unicode to locale conversion helpers.
+ *
+ * Copyright (C) 2003-2006 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Derk-Jan Hartman <thedj at users.sf.net>
+ *          Christophe Massiot
+ *          Rémi Denis-Courmont
+ *
+ * vlc_current_charset() an adaption of mp_locale_charset():
+ *
+ *  Copyright (C) 2001-2003 The Mape Project
+ *  Written by Karel Zak  <zakkr@zf.jcu.cz>.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <vlc/vlc.h>
+
+#if !defined WIN32
+# if HAVE_LANGINFO_CODESET
+#  include <langinfo.h>
+# endif
+# include <locale.h>
+#else
+# include <windows.h>
+#endif
+
+#ifdef __APPLE__
+#   include <errno.h>
+#   include <string.h>
+#endif
+
+#include "charset.h"
+
+typedef struct VLCCharsetAlias
+{
+    char *psz_alias, *psz_name;
+} VLCCharsetAlias;
+
+/*
+ * The libcharset load all from external text file, but it's strange and
+ * slow solution, we rather use array(s) compiled into source. In the
+ * "good" libc this is not needful -- for example in linux.
+ *
+ * Please, put to this funtion exotic aliases only. The libc 'iconv' knows
+ * a lot of basic aliases (check it first by iconv -l).
+ *
+ */
+#if (defined OS2 || !HAVE_LANGINFO_CODESET) && !defined WIN32
+static const char* vlc_encoding_from_language( const char *l )
+{
+    /* check for language (and perhaps country) codes */
+    if (strstr(l, "zh_TW")) return "Big5";
+    if (strstr(l, "zh_HK")) return "Big5HKSCS";   /* no MIME charset */
+    if (strstr(l, "zh")) return "GB2312";
+    if (strstr(l, "th")) return "TIS-620";
+    if (strstr(l, "ja")) return "EUC-JP";
+    if (strstr(l, "ko")) return "EUC-KR";
+    if (strstr(l, "ru")) return "KOI8-R";
+    if (strstr(l, "uk")) return "KOI8-U";
+    if (strstr(l, "pl") || strstr(l, "hr") ||
+        strstr(l, "hu") || strstr(l, "cs") ||
+        strstr(l, "sk") || strstr(l, "sl")) return "ISO-8859-2";
+    if (strstr(l, "eo") || strstr(l, "mt")) return "ISO-8859-3";
+    if (strstr(l, "lt") || strstr(l, "la")) return "ISO-8859-4";
+    if (strstr(l, "bg") || strstr(l, "be") ||
+        strstr(l, "mk") || strstr(l, "uk")) return "ISO-8859-5";
+    if (strstr(l, "ar")) return "ISO-8859-6";
+    if (strstr(l, "el")) return "ISO-8859-7";
+    if (strstr(l, "he") || strstr(l, "iw")) return "ISO-8859-8";
+    if (strstr(l, "tr")) return "ISO-8859-9";
+    if (strstr(l, "th")) return "ISO-8859-11";
+    if (strstr(l, "lv")) return "ISO-8859-13";
+    if (strstr(l, "cy")) return "ISO-8859-14";
+    if (strstr(l, "et")) return "ISO-8859-15"; /* all latin1 could be iso15 as well */
+    if (strstr(l, "ro")) return "ISO-8859-2";   /* or ISO-8859-16 */
+    if (strstr(l, "am") || strstr(l, "vi")) return "UTF-8";
+    /* We don't know. This ain't working go to default. */
+    return "ISO-8859-1";
+}
+#endif
+
+static const char* vlc_charset_aliases( const char *psz_name )
+{
+    VLCCharsetAlias     *a;
+
+#if defined WIN32
+    VLCCharsetAlias aliases[] =
+    {
+        { "CP936",      "GBK" },
+        { "CP1361",     "JOHAB" },
+        { "CP20127",    "ASCII" },
+        { "CP20866",    "KOI8-R" },
+        { "CP21866",    "KOI8-RU" },
+        { "CP28591",    "ISO-8859-1" },
+        { "CP28592",    "ISO-8859-2" },
+        { "CP28593",    "ISO-8859-3" },
+        { "CP28594",    "ISO-8859-4" },
+        { "CP28595",    "ISO-8859-5" },
+        { "CP28596",    "ISO-8859-6" },
+        { "CP28597",    "ISO-8859-7" },
+        { "CP28598",    "ISO-8859-8" },
+        { "CP28599",    "ISO-8859-9" },
+        { "CP28605",    "ISO-8859-15" },
+        { NULL,         NULL }
+    };
+#elif defined (SYS_AIX)
+    VLCCharsetAlias aliases[] =
+    {
+        { "IBM-850",    "CP850" },
+        { "IBM-856",    "CP856" },
+        { "IBM-921",    "ISO-8859-13" },
+        { "IBM-922",    "CP922" },
+        { "IBM-932",    "CP932" },
+        { "IBM-943",    "CP943" },
+        { "IBM-1046",   "CP1046" },
+        { "IBM-1124",   "CP1124" },
+        { "IBM-1129",   "CP1129" },
+        { "IBM-1252",   "CP1252" },
+        { "IBM-EUCCN",  "GB2312" },
+        { "IBM-EUCJP",  "EUC-JP" },
+        { "IBM-EUCKR",  "EUC-KR" },
+        { "IBM-EUCTW",  "EUC-TW" },
+        { NULL, NULL }
+    };
+#elif defined (SYS_HPUX)
+    VLCCharsetAlias aliases[] =
+    {
+        { "ROMAN8",     "HP-ROMAN8" },
+        { "ARABIC8",    "HP-ARABIC8" },
+        { "GREEK8",     "HP-GREEK8" },
+        { "HEBREW8",    "HP-HEBREW8" },
+        { "TURKISH8",   "HP-TURKISH8" },
+        { "KANA8",      "HP-KANA8" },
+        { "HP15CN",     "GB2312" },
+        { NULL, NULL }
+    };
+#elif defined (SYS_IRIX)
+    VLCCharsetAlias aliases[] =
+    {
+        { "EUCCN",      "GB2312" },
+        { NULL, NULL }
+    };
+#elif defined (SYS_OSF)
+    VLCCharsetAlias aliases[] =
+    {
+        { "KSC5601",    "CP949" },
+        { "SDECKANJI",  "EUC-JP" },
+        { "TACTIS",     "TIS-620" },
+        { NULL, NULL }
+    };
+#elif defined (SYS_SOLARIS)
+    VLCCharsetAlias aliases[] =
+    {
+        { "646", "ASCII" },
+        { "CNS11643",   "EUC-TW" },
+        { "5601",       "EUC-KR" },
+        { "JOHAP92",    "JOHAB" },
+        { "PCK", "SHIFT_JIS" },
+        { "2533",       "TIS-620" },
+        { NULL, NULL }
+    };
+#elif defined (SYS_BSD)
+    VLCCharsetAlias aliases[] =
+    {
+        { "646", " ASCII" },
+        { "EUCCN", "GB2312" },
+        { NULL, NULL }
+    };
+#else
+    VLCCharsetAlias aliases[] = {{NULL, NULL}};
+#endif
+
+    for (a = aliases; a->psz_alias; a++)
+        if (strcasecmp (a->psz_alias, psz_name) == 0)
+            return a->psz_name;
+
+    /* we return original name beacuse iconv() probably will know
+     * something better about name if we don't know it :-) */
+    return psz_name;
+}
+
+/* Returns charset from "language_COUNTRY.charset@modifier" string */
+#if (defined OS2 || !HAVE_LANGINFO_CODESET) && !defined WIN32
+static void vlc_encoding_from_locale( char *psz_locale, char *psz_charset )
+{
+    char *psz_dot = strchr( psz_locale, '.' );
+
+    if( psz_dot != NULL )
+    {
+        const char *psz_modifier;
+
+        psz_dot++;
+
+        /* Look for the possible @... trailer and remove it, if any.  */
+        psz_modifier = strchr( psz_dot, '@' );
+
+        if( psz_modifier == NULL )
+        {
+            strcpy( psz_charset, psz_dot );
+            return;
+        }
+        if( 0 < ( psz_modifier - psz_dot )
+             && ( psz_modifier - psz_dot ) < 2 + 10 + 1 )
+        {
+            memcpy( psz_charset, psz_dot, psz_modifier - psz_dot );
+            psz_charset[ psz_modifier - psz_dot ] = '\0';
+            return;
+        }
+    }
+    /* try language mapping */
+    strcpy( psz_charset, vlc_encoding_from_language( psz_locale ) );
+}
+#endif
+
+vlc_bool_t vlc_current_charset( char **psz_charset )
+{
+    const char *psz_codeset;
+
+#if !(defined WIN32 || defined OS2 || defined __APPLE__)
+
+# if HAVE_LANGINFO_CODESET
+    /* Most systems support nl_langinfo( CODESET ) nowadays.  */
+    psz_codeset = nl_langinfo( CODESET );
+    if( !strcmp( psz_codeset, "ANSI_X3.4-1968" ) )
+        psz_codeset = "ASCII";
+# else
+    /* On old systems which lack it, use setlocale or getenv.  */
+    const char *psz_locale = NULL;
+    char buf[2 + 10 + 1];
+
+    /* But most old systems don't have a complete set of locales.  Some
+     * (like SunOS 4 or DJGPP) have only the C locale.  Therefore we don't
+     * use setlocale here; it would return "C" when it doesn't support the
+     * locale name the user has set. Darwin's setlocale is broken. */
+#  if HAVE_SETLOCALE && !__APPLE__
+    psz_locale = setlocale( LC_ALL, NULL );
+#  endif
+    if( psz_locale == NULL || psz_locale[0] == '\0' )
+    {
+        psz_locale = getenv( "LC_ALL" );
+        if( psz_locale == NULL || psz_locale[0] == '\0' )
+        {
+            psz_locale = getenv( "LC_CTYPE" );
+            if( psz_locale == NULL || psz_locale[0] == '\0')
+                psz_locale = getenv( "LANG" );
+        }
+    }
+
+    /* On some old systems, one used to set locale = "iso8859_1". On others,
+     * you set it to "language_COUNTRY.charset". Darwin only has LANG :( */
+    vlc_encoding_from_locale( (char *)psz_locale, buf );
+    psz_codeset =  buf;
+# endif /* HAVE_LANGINFO_CODESET */
+
+#elif defined __APPLE__
+
+    /* Darwin is always using UTF-8 internally. */
+    psz_codeset = "UTF-8";
+
+#elif defined WIN32
+
+    char buf[2 + 10 + 1];
+
+    /* Woe32 has a function returning the locale's codepage as a number.  */
+    snprintf( buf, sizeof( buf ), "CP%u", GetACP() );
+    psz_codeset = buf;
+
+#elif defined OS2
+
+    const char *psz_locale;
+    char buf[2 + 10 + 1];
+    ULONG cp[3];
+    ULONG cplen;
+
+    /* Allow user to override the codeset, as set in the operating system,
+     * with standard language environment variables. */
+    psz_locale = getenv( "LC_ALL" );
+    if( psz_locale == NULL || psz_locale[0] == '\0' )
+    {
+        psz+locale = getenv( "LC_CTYPE" );
+        if( psz_locale == NULL || locale[0] == '\0' )
+            locale = getenv( "LANG" );
+    }
+    if( psz_locale != NULL && psz_locale[0] != '\0' )
+        vlc_encoding_from_locale( psz_locale, buf );
+        psz_codeset = buf;
+    else
+    {
+        /* OS/2 has a function returning the locale's codepage as a number. */
+        if( DosQueryCp( sizeof( cp ), cp, &cplen ) )
+            psz_codeset = "";
+        else
+        {
+            snprintf( buf, sizeof( buf ), "CP%u", cp[0] );
+            psz_codeset = buf;
+        }
+    }
+#endif
+    if( psz_codeset == NULL )
+        /* The canonical name cannot be determined. */
+        psz_codeset = "";
+    else
+        psz_codeset = vlc_charset_aliases( psz_codeset );
+
+    /* Don't return an empty string.  GNU libc and GNU libiconv interpret
+     * the empty string as denoting "the locale's character encoding",
+     * thus GNU libiconv would call this function a second time. */
+    if( psz_codeset[0] == '\0' )
+    {
+        /* Last possibility is 'CHARSET' enviroment variable */
+        if( !( psz_codeset = getenv( "CHARSET" ) ) )
+            psz_codeset = "ISO-8859-1";
+    }
+
+    if( psz_charset )
+        *psz_charset = strdup(psz_codeset);
+
+    if( !strcasecmp(psz_codeset, "UTF8") || !strcasecmp(psz_codeset, "UTF-8") )
+        return VLC_TRUE;
+
+    return VLC_FALSE;
+}
+
+
+char *vlc_fix_readdir( const char *psz_string )
+{
+#ifdef __APPLE__
+    vlc_iconv_t hd = vlc_iconv_open( "UTF-8", "UTF-8-MAC" );
+
+    if (hd != (vlc_iconv_t)(-1))
+    {
+        const char *psz_in = psz_string;
+        size_t i_in = strlen(psz_in);
+        size_t i_out = i_in * 2;
+        char *psz_utf8 = malloc(i_out + 1);
+        char *psz_out = psz_utf8;
+
+        size_t i_ret = vlc_iconv (hd, &psz_in, &i_in, &psz_out, &i_out);
+        vlc_iconv_close (hd);
+        if( i_ret == (size_t)(-1) || i_in )
+        {
+            free( psz_utf8 );
+            return strdup( psz_string );
+        }
+
+        *psz_out = '\0';
+        return psz_utf8;
+    }
+#else
+    return strdup( psz_string );
+#endif
+}
+
+
+static inline int locale_match (const char *tab, const char *locale)
+{
+    for (;*tab; tab += 2)
+        if (memcmp (tab, locale, 2) == 0)
+            return 0;
+    return 1;
+}
+
+
+/**
+ * @return a fallback characters encoding to be used, given a locale.
+ */
+static const char *FindFallbackEncoding (const char *locale)
+{
+    if ((locale == NULL) || (strlen (locale) < 2))
+        return "ASCII";
+
+
+    /*** The ISO-8859 series (anything but Asia) ***/
+    // Latin-1 Western-European languages (ISO-8859-1)
+    static const char western[] =
+        "aa" "af" "an" "br" "ca" "da" "de" "en" "es" "et" "eu" "fi" "fo" "fr"
+        "ga" "gd" "gl" "gv" "id" "is" "it" "kl" "kw" "mg" "ms" "nb" "nl" "nn"
+        "no" "oc" "om" "pt" "so" "sq" "st" "sv" "tl" "uz" "wa" "xh" "zu"
+        "eo" "mt" "cy";
+    if (!locale_match (western, locale))
+        return "CP1252"; // Compatible Microsoft superset
+
+    // Latin-2 Slavic languages (ISO-8859-2)
+    static const char slavic[] = "bs" "cs" "hr" "hu" "pl" "ro" "sk" "sl";
+    if (!locale_match (slavic, locale))
+        return "CP1250"; // CP1250 is more common, but incompatible
+
+    // Latin-3 Southern European languages (ISO-8859-3)
+    // "eo" and "mt" -> Latin-1 instead, I presume(?).
+    // "tr" -> ISO-8859-9 instead
+
+    // Latin-4 North-European languages (ISO-8859-4)
+    // -> Latin-1 instead
+
+    /* Cyrillic alphabet languages (ISO-8859-5) */
+    static const char cyrillic[] = "be" "bg" "mk" "ru" "sr";
+    if (!locale_match (cyrillic, locale))
+        return "CP1251"; // KOI8, ISO-8859-5 and CP1251 are incompatible(?)
+
+    /* Arabic (ISO-8859-6) */
+    if (!locale_match ("ar", locale))
+        // FIXME: someone check if we should return CP1256 or ISO-8859-6
+        return "CP1256"; // CP1256 is(?) more common, but incompatible(?)
+
+    /* Greek (ISO-8859-7) */
+    if (!locale_match ("el", locale))
+        // FIXME: someone check if we should return CP1253 or ISO-8859-7
+        return "CP1253"; // CP1253 is(?) more common and less incompatible
+
+    /* Hebrew (ISO-8859-8) */
+    if (!locale_match ("he" "iw" "yi", locale))
+        return "CP1255"; // Compatible Microsoft superset
+
+    /* Latin-5 Turkish (ISO-8859-9) */
+    if (!locale_match ("tr" "ku", locale))
+        return "CP1254"; // Compatible Microsoft superset
+
+    /* Latin-6 “North-European” languages (ISO-8859-10) */
+    /* It is so much north European that glibc only uses that for Luganda
+     * which is spoken in Uganda... unless someone complains, I'm not
+     * using this one; let's fallback to CP1252 here. */
+
+    // ISO-8859-11 does arguably not exist. Thai is handled below.
+
+    // ISO-8859-12 really doesn't exist.
+
+    // Latin-7 Baltic languages (ISO-8859-13)
+    if (!locale_match ("lt" "lv" "mi", locale))
+        // FIXME: mi = New Zealand, doesn't sound baltic!
+        return "CP1257"; // Compatible Microsoft superset
+
+    // Latin-8 Celtic languages (ISO-8859-14)
+    // "cy" -> use Latin-1 instead (most likely English or French)
+
+    // Latin-9 (ISO-8859-15) -> see Latin-1
+
+    // Latin-10 (ISO-8859-16) does not seem to be used
+
+    /*** KOI series ***/
+    // For Russian, we use CP1251
+    if (!locale_match ("uk", locale))
+        return "KOI8-U";
+
+    if (!locale_match ("tg", locale))
+        return "KOI8-T";
+
+    /*** Asia ***/
+    // Japanese
+    if (!locale_match ("jp", locale))
+        return "SHIFT-JIS"; // Shift-JIS is way more common than EUC-JP
+
+    // Korean
+    if (!locale_match ("ko", locale))
+        return "EUC-KR";
+
+    // Thai
+    if (!locale_match ("th", locale))
+        return "TIS-620";
+
+    // Vietnamese (FIXME: more infos needed)
+    if (!locale_match ("vt", locale))
+        /* VISCII is probably a bad idea as it is not extended ASCII */
+        /* glibc has TCVN5712-1 */
+        return "CP1258";
+
+    /* Kazakh (FIXME: more infos needed) */
+    if (!locale_match ("kk", locale))
+        return "PT154";
+
+    // Chinese. The politically incompatible character sets.
+    if (!locale_match ("zh", locale))
+    {
+        if ((strlen (locale) >= 5) && (locale[2] != '_'))
+            locale += 3;
+
+        // Hong Kong
+        if (!locale_match ("HK", locale))
+            return "BIG5-HKSCS"; /* FIXME: use something else? */
+
+        // Taiwan island
+        if (!locale_match ("TW", locale))
+            return "BIG5";
+
+        // People's Republic of China and Singapore
+        /*
+         * GB18030 can represent any Unicode code point
+         * (like UTF-8), while remaining compatible with GBK
+         * FIXME: is it compatible with GB2312? if not, should we
+         * use GB2312 instead?
+         */
+        return "GB18030";
+    }
+
+    return "ASCII";
+}
+
+/**
+ * GetFallbackEncoding() suggests an encoding to be used for non UTF-8
+ * text files accord to the system's local settings. It is only a best
+ * guess.
+ */
+const char *GetFallbackEncoding( void )
+{
+#ifndef WIN32
+    const char *psz_lang = NULL;
+
+    /* Some systems (like Darwin, SunOS 4 or DJGPP) have only the C locale.
+     * Therefore we don't use setlocale here; it would return "C". */
+#  if defined (HAVE_SETLOCALE) && !defined ( __APPLE__)
+    psz_lang = setlocale( LC_ALL, NULL );
+#  endif
+    if( psz_lang == NULL || psz_lang[0] == '\0' )
+    {
+        psz_lang = getenv( "LC_ALL" );
+        if( psz_lang == NULL || psz_lang == '\0' )
+        {
+            psz_lang = getenv( "LC_CTYPE" );
+            if( psz_lang == NULL || psz_lang[0] == '\0')
+                psz_lang = getenv( "LANG" );
+        }
+    }
+
+    return FindFallbackEncoding( psz_lang );
+#else
+    /*
+     * This should be thread-safe given GetACP() should always return
+     * the same result.
+     */
+    static char buf[2 + 10 + 1] = "";
+
+    if( buf[0] == 0 )
+        snprintf( buf, sizeof( buf ), "CP%u", GetACP() );
+    return buf;
+#endif
+}
+
+/**
+ * There are two decimal separators in the computer world-wide locales:
+ * dot (which is the american default), and comma (which is used in France,
+ * the country with the most VLC developers, among others).
+ *
+ * i18n_strtod() has the same prototype as ANSI C strtod() but it accepts
+ * either decimal separator when deserializing the string to a float number,
+ * independant of the local computer setting.
+ */
+double i18n_strtod( const char *str, char **end )
+{
+    char *end_buf, e;
+    double d;
+
+    if( end == NULL )
+        end = &end_buf;
+    d = strtod( str, end );
+
+    e = **end;
+    if(( e == ',' ) || ( e == '.' ))
+    {
+        char dup[strlen( str ) + 1];
+        strcpy( dup, str );
+
+        if( dup == NULL )
+            return d;
+
+        dup[*end - str] = ( e == ',' ) ? '.' : ',';
+        d = strtod( dup, end );
+    }
+    return d;
+}
+
+/**
+ * i18n_atof() has the same prototype as ANSI C atof() but it accepts
+ * either decimal separator when deserializing the string to a float number,
+ * independant of the local computer setting.
+ */
+double i18n_atof( const char *str )
+{
+    return i18n_strtod( str, NULL );
+}
+
+
+/**
+ * us_strtod() has the same prototype as ANSI C strtod() but it expects
+ * a dot as decimal separator regardless of the system locale.
+ */
+double us_strtod( const char *str, char **end )
+{
+    char dup[strlen( str ) + 1], *ptr;
+    double d;
+    strcpy( dup, str );
+
+    ptr = strchr( dup, ',' );
+    if( ptr != NULL )
+        *ptr = '\0';
+
+    d = strtod( dup, &ptr );
+    if( end != NULL )
+        *end = (char *)&str[ptr - dup];
+
+    return d;
+}
+
+/**
+ * us_atof() has the same prototype as ANSI C atof() but it expects a dot
+ * as decimal separator, regardless of the system locale.
+ */
+double us_atof( const char *str )
+{
+    return us_strtod( str, NULL );
+}
+
diff --git a/src/text/iso-639_def.h b/src/text/iso-639_def.h
new file mode 100644 (file)
index 0000000..829ccf6
--- /dev/null
@@ -0,0 +1,205 @@
+/*****************************************************************************
+ * iso_lang.h: languages codes and abbreviations
+ *****************************************************************************
+ * Copyright (C) 1998-2004 the VideoLAN team
+ * $Id$
+ *
+ * This is used in iso_lang.cpp and is taken from the GNU glibc 2.2.5
+ * tarball. It has been partially completed with native language names.
+ * Authors: Stéphane Borel <stef@via.ecp.fr>
+ *          Arnaud de Bossoreille de Ribou <bozo@via.ecp.fr>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/* Define the languages codes and abbreviations according to ISO 639-[12].
+ * Format is:( "English name", 639-1-code, 639-2/T-code, 639-2/B-code)
+ * If you find something missing or wrong contact <bug-glibc@gnu.org> */
+
+/* Some spellings were converted to pure ASCII:
+ *  Provençal -> Provencal
+ *  Volapk -> Volapuk
+ *  Bokm? -> Bokmaal */
+
+static const iso639_lang_t p_languages[] =
+{
+    { N_( "Afar" ),             "",            "aa", "aar", "aar" },
+    { N_( "Abkhazian" ),        "",            "ab", "abk", "abk" },
+    { N_( "Afrikaans" ),        "",            "af", "afr", "afr" },
+    { N_( "Albanian" ),         "",            "sq", "sqi", "alb" },
+    { N_( "Amharic" ),          "",            "am", "amh", "amh" },
+    { N_( "Arabic" ),           "",            "ar", "ara", "ara" },
+    { N_( "Armenian" ),         "",            "hy", "hye", "arm" },
+    { N_( "Assamese" ),         "",            "as", "asm", "asm" },
+    { N_( "Avestan" ),          "",            "ae", "ave", "ave" },
+    { N_( "Aymara" ),           "",            "ay", "aym", "aym" },
+    { N_( "Azerbaijani" ),      "",            "az", "aze", "aze" },
+    { N_( "Bashkir" ),          "",            "ba", "bak", "bak" },
+    { N_( "Basque" ),           "",            "eu", "eus", "baq" },
+    { N_( "Belarusian" ),       "",            "be", "bel", "bel" },
+    { N_( "Bengali" ),          "",            "bn", "ben", "ben" },
+    { N_( "Bihari" ),           "",            "bh", "bih", "bih" },
+    { N_( "Bislama" ),          "",            "bi", "bis", "bis" },
+    { N_( "Bosnian" ),          "",            "bs", "bos", "bos" },
+    { N_( "Breton" ),           "",            "br", "bre", "bre" },
+    { N_( "Bulgarian" ),        "",            "bg", "bul", "bul" },
+    { N_( "Burmese" ),          "",            "my", "mya", "bur" },
+    { N_( "Catalan" ),          "Catala",      "ca", "cat", "cat" },
+    { N_( "Chamorro" ),         "",            "ch", "cha", "cha" },
+    { N_( "Chechen" ),          "",            "ce", "che", "che" },
+    { N_( "Chinese" ),          "",            "zh", "zho", "chi" },
+    { N_( "Church Slavic" ),    "",            "cu", "chu", "chu" },
+    { N_( "Chuvash" ),          "",            "cv", "chv", "chv" },
+    { N_( "Cornish" ),          "",            "kw", "cor", "cor" },
+    { N_( "Corsican" ),         "",            "co", "cos", "cos" },
+    { N_( "Czech" ),            "",            "cs", "ces", "cze" },
+    { N_( "Danish" ),           "Dansk",       "da", "dan", "dan" },
+    { N_( "Dutch" ),            "Nederlands",  "nl", "nld", "dut" },
+    { N_( "Dzongkha" ),         "",            "dz", "dzo", "dzo" },
+    { N_( "English" ),          "English",     "en", "eng", "eng" },
+    { N_( "Esperanto" ),        "",            "eo", "epo", "epo" },
+    { N_( "Estonian" ),         "",            "et", "est", "est" },
+    { N_( "Faroese" ),          "",            "fo", "fao", "fao" },
+    { N_( "Fijian" ),           "",            "fj", "fij", "fij" },
+    { N_( "Finnish" ),          "Suomi",       "fi", "fin", "fin" },
+    { N_( "French" ),           "Francais",    "fr", "fra", "fre" },
+    { N_( "Frisian" ),          "",            "fy", "fry", "fry" },
+    { N_( "Georgian" ),         "",            "ka", "kat", "geo" },
+    { N_( "German" ),           "Deutsch",     "de", "deu", "ger" },
+    { N_( "Gaelic (Scots)" ),   "",            "gd", "gla", "gla" },
+    { N_( "Irish" ),            "",            "ga", "gle", "gle" },
+    { N_( "Gallegan" ),         "",            "gl", "glg", "glg" },
+    { N_( "Manx" ),             "",            "gv", "glv", "glv" },
+    { N_( "Greek, Modern ()" ), "",            "el", "gre", "ell" },
+    { N_( "Guarani" ),          "",            "gn", "grn", "grn" },
+    { N_( "Gujarati" ),         "",            "gu", "guj", "guj" },
+    { N_( "Hebrew" ),           "",            "he", "heb", "heb" },
+    { N_( "Herero" ),           "",            "hz", "her", "her" },
+    { N_( "Hindi" ),            "",            "hi", "hin", "hin" },
+    { N_( "Hiri Motu" ),        "",            "ho", "hmo", "hmo" },
+    { N_( "Hungarian" ),        "Magyar",      "hu", "hun", "hun" },
+    { N_( "Icelandic" ),        "Islenska",    "is", "isl", "ice" },
+    { N_( "Inuktitut" ),        "",            "iu", "iku", "iku" },
+    { N_( "Interlingue" ),      "",            "ie", "ile", "ile" },
+    { N_( "Interlingua" ),      "",            "ia", "ina", "ina" },
+    { N_( "Indonesian" ),       "",            "id", "ind", "ind" },
+    { N_( "Inupiaq" ),          "",            "ik", "ipk", "ipk" },
+    { N_( "Italian" ),          "Italiano",    "it", "ita", "ita" },
+    { N_( "Javanese" ),         "",            "jv", "jaw", "jav" },
+    { N_( "Japanese" ),         "",            "ja", "jpn", "jpn" },
+    { N_( "Kalaallisut (Greenlandic)" ), "",   "kl", "kal", "kal" },
+    { N_( "Kannada" ),          "",            "kn", "kan", "kan" },
+    { N_( "Kashmiri" ),         "",            "ks", "kas", "kas" },
+    { N_( "Kazakh" ),           "",            "kk", "kaz", "kaz" },
+    { N_( "Khmer" ),            "",            "km", "khm", "khm" },
+    { N_( "Kikuyu" ),           "",            "ki", "kik", "kik" },
+    { N_( "Kinyarwanda" ),      "",            "rw", "kin", "kin" },
+    { N_( "Kirghiz" ),          "",            "ky", "kir", "kir" },
+    { N_( "Komi" ),             "",            "kv", "kom", "kom" },
+    { N_( "Korean" ),           "",            "ko", "kor", "kor" },
+    { N_( "Kuanyama" ),         "",            "kj", "kua", "kua" },
+    { N_( "Kurdish" ),          "",            "ku", "kur", "kur" },
+    { N_( "Lao" ),              "",            "lo", "lao", "lao" },
+    { N_( "Latin" ),            "",            "la", "lat", "lat" },
+    { N_( "Latvian" ),          "",            "lv", "lav", "lav" },
+    { N_( "Lingala" ),          "",            "ln", "lin", "lin" },
+    { N_( "Lithuanian" ),       "",            "lt", "lit", "lit" },
+    { N_( "Letzeburgesch" ),    "",            "lb", "ltz", "ltz" },
+    { N_( "Macedonian" ),       "",            "mk", "mkd", "mac" },
+    { N_( "Marshall" ),         "",            "mh", "mah", "mah" },
+    { N_( "Malayalam" ),        "",            "ml", "mal", "mal" },
+    { N_( "Maori" ),            "",            "mi", "mri", "mao" },
+    { N_( "Marathi" ),          "",            "mr", "mar", "mar" },
+    { N_( "Malay" ),            "",            "ms", "msa", "may" },
+    { N_( "Malagasy" ),         "",            "mg", "mlg", "mlg" },
+    { N_( "Maltese" ),          "",            "mt", "mlt", "mlt" },
+    { N_( "Moldavian" ),        "",            "mo", "mol", "mol" },
+    { N_( "Mongolian" ),        "",            "mn", "mon", "mon" },
+    { N_( "Nauru" ),            "",            "na", "nau", "nau" },
+    { N_( "Navajo" ),           "",            "nv", "nav", "nav" },
+    { N_( "Ndebele, South" ),   "",            "nr", "nbl", "nbl" },
+    { N_( "Ndebele, North" ),   "",            "nd", "nde", "nde" },
+    { N_( "Ndonga" ),           "",            "ng", "ndo", "ndo" },
+    { N_( "Nepali" ),           "",            "ne", "nep", "nep" },
+    { N_( "Norwegian" ),        "Norsk",       "no", "nor", "nor" },
+    { N_( "Norwegian Nynorsk" ), "",           "nn", "nno", "nno" },
+    { N_( "Norwegian Bokmaal" ), "",           "nb", "nob", "nob" },
+    { N_( "Chichewa; Nyanja" ), "",            "ny", "nya", "nya" },
+    { N_( "Occitan (post 1500); Provencal" ), "", "oc", "oci", "oci" },
+    { N_( "Oriya" ),            "",            "or", "ori", "ori" },
+    { N_( "Oromo" ),            "",            "om", "orm", "orm" },
+    { N_( "On Screen Display" ),"On Screen Display", "od", "osd", "osd" },
+    { N_( "Ossetian; Ossetic" ), "",           "os", "oss", "oss" },
+    { N_( "Panjabi" ),          "",            "pa", "pan", "pan" },
+    { N_( "Persian" ),          "",            "fa", "fas", "per" },
+    { N_( "Pali" ),             "",            "pi", "pli", "pli" },
+    { N_( "Polish" ),           "",            "pl", "pol", "pol" },
+    { N_( "Portuguese" ),       "Portugues",   "pt", "por", "por" },
+    { N_( "Pushto" ),           "",            "ps", "pus", "pus" },
+    { N_( "Quechua" ),          "",            "qu", "que", "que" },
+    { N_( "Raeto-Romance" ),    "",            "rm", "roh", "roh" },
+    { N_( "Romanian" ),         "",            "ro", "ron", "rum" },
+    { N_( "Rundi" ),            "",            "rn", "run", "run" },
+    { N_( "Russian" ),          "",            "ru", "rus", "rus" },
+    { N_( "Sango" ),            "",            "sg", "sag", "sag" },
+    { N_( "Sanskrit" ),         "",            "sa", "san", "san" },
+    { N_( "Serbian" ),          "",            "sr", "srp", "scc" },
+    { N_( "Croatian" ),         "Hrvatski",    "hr", "hrv", "scr" },
+    { N_( "Sinhalese" ),        "",            "si", "sin", "sin" },
+    { N_( "Slovak" ),           "",            "sk", "slk", "slo" },
+    { N_( "Slovenian" ),        "",            "sl", "slv", "slv" },
+    { N_( "Northern Sami" ),    "",            "se", "sme", "sme" },
+    { N_( "Samoan" ),           "",            "sm", "smo", "smo" },
+    { N_( "Shona" ),            "",            "sn", "sna", "sna" },
+    { N_( "Sindhi" ),           "",            "sd", "snd", "snd" },
+    { N_( "Somali" ),           "",            "so", "som", "som" },
+    { N_( "Sotho, Southern" ),  "",            "st", "sot", "sot" },
+    { N_( "Spanish" ),          "Espanol",     "es", "spa", "spa" },
+    { N_( "Sardinian" ),        "",            "sc", "srd", "srd" },
+    { N_( "Swati" ),            "",            "ss", "ssw", "ssw" },
+    { N_( "Sundanese" ),        "",            "su", "sun", "sun" },
+    { N_( "Swahili" ),          "",            "sw", "swa", "swa" },
+    { N_( "Swedish" ),          "Svenska",     "sv", "swe", "swe" },
+    { N_( "Tahitian" ),         "",            "ty", "tah", "tah" },
+    { N_( "Tamil" ),            "",            "ta", "tam", "tam" },
+    { N_( "Tatar" ),            "",            "tt", "tat", "tat" },
+    { N_( "Telugu" ),           "",            "te", "tel", "tel" },
+    { N_( "Tajik" ),            "",            "tg", "tgk", "tgk" },
+    { N_( "Tagalog" ),          "",            "tl", "tgl", "tgl" },
+    { N_( "Thai" ),             "",            "th", "tha", "tha" },
+    { N_( "Tibetan" ),          "",            "bo", "bod", "tib" },
+    { N_( "Tigrinya" ),         "",            "ti", "tir", "tir" },
+    { N_( "Tonga (Tonga Islands)" ), "",       "to", "ton", "ton" },
+    { N_( "Tswana" ),           "",            "tn", "tsn", "tsn" },
+    { N_( "Tsonga" ),           "",            "ts", "tso", "tso" },
+    { N_( "Turkish" ),          "",            "tr", "tur", "tur" },
+    { N_( "Turkmen" ),          "",            "tk", "tuk", "tuk" },
+    { N_( "Twi" ),              "",            "tw", "twi", "twi" },
+    { N_( "Uighur" ),           "",            "ug", "uig", "uig" },
+    { N_( "Ukrainian" ),        "",            "uk", "ukr", "ukr" },
+    { N_( "Urdu" ),             "",            "ur", "urd", "urd" },
+    { N_( "Uzbek" ),            "",            "uz", "uzb", "uzb" },
+    { N_( "Vietnamese" ),       "",            "vi", "vie", "vie" },
+    { N_( "Volapuk" ),          "",            "vo", "vol", "vol" },
+    { N_( "Welsh" ),            "",            "cy", "cym", "wel" },
+    { N_( "Wolof" ),            "",            "wo", "wol", "wol" },
+    { N_( "Xhosa" ),            "",            "xh", "xho", "xho" },
+    { N_( "Yiddish" ),          "",            "yi", "yid", "yid" },
+    { N_( "Yoruba" ),           "",            "yo", "yor", "yor" },
+    { N_( "Zhuang" ),           "",            "za", "zha", "zha" },
+    { N_( "Zulu" ),             "",            "zu", "zul", "zul" },
+    { NULL, NULL, NULL, NULL, NULL }
+};
+
diff --git a/src/text/iso_lang.c b/src/text/iso_lang.c
new file mode 100644 (file)
index 0000000..30c011b
--- /dev/null
@@ -0,0 +1,105 @@
+/*****************************************************************************
+ * iso_lang.c: function to decode language code (in dvd or a52 for instance).
+ *****************************************************************************
+ * Copyright (C) 1998-2004 the VideoLAN team
+ * $Id$
+ *
+ * Author: Stéphane Borel <stef@via.ecp.fr>
+ *         Arnaud de Bossoreille de Ribou <bozo@via.ecp.fr>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#include <stdio.h>
+
+#include <vlc/vlc.h>
+
+#include "iso_lang.h"
+
+/*****************************************************************************
+ * Local tables
+ *****************************************************************************/
+#include "iso-639_def.h"
+
+static const iso639_lang_t unknown_language =
+    { "Unknown", "Unknown", "??", "???", "???" };
+
+/*****************************************************************************
+ * DecodeLanguage: gives the long language name from the two-letter
+ *                 ISO-639 code
+ *****************************************************************************/
+const char * DecodeLanguage( uint16_t i_code )
+{
+    const iso639_lang_t * p_lang;
+    uint8_t psz_code[3];
+
+    psz_code[0] = i_code >> 8;
+    psz_code[1] = i_code & 0xff;
+    psz_code[2] = '\0';
+
+    for( p_lang = p_languages; p_lang->psz_eng_name; p_lang++ )
+    {
+        if( !memcmp( p_lang->psz_iso639_1, psz_code, 2 ) )
+        {
+# if 0
+            if( *p_lang->psz_native_name )
+            {
+                return p_lang->psz_native_name;
+            }
+#endif
+
+            return _( p_lang->psz_eng_name );
+        }
+    }
+
+    return _( "Unknown" );
+}
+
+const iso639_lang_t * GetLang_1( const char * psz_code )
+{
+    const iso639_lang_t *p_lang;
+
+    for( p_lang = p_languages; p_lang->psz_eng_name; p_lang++ )
+        if( !strncmp( p_lang->psz_iso639_1, psz_code, 2 ) )
+            return p_lang;
+
+    return &unknown_language;
+}
+
+const iso639_lang_t * GetLang_2T( const char * psz_code )
+{
+    const iso639_lang_t *p_lang;
+
+    for( p_lang = p_languages; p_lang->psz_eng_name; p_lang++ )
+        if( !strncmp( p_lang->psz_iso639_2T, psz_code, 3 ) )
+            return p_lang;
+
+    return &unknown_language;
+}
+
+const iso639_lang_t * GetLang_2B( const char * psz_code )
+{
+    const iso639_lang_t *p_lang;
+
+    for( p_lang = p_languages; p_lang->psz_eng_name; p_lang++ )
+        if( !strncmp( p_lang->psz_iso639_2B, psz_code, 3 ) )
+            return p_lang;
+
+    return &unknown_language;
+}
+
diff --git a/src/text/strings.c b/src/text/strings.c
new file mode 100644 (file)
index 0000000..f6e1423
--- /dev/null
@@ -0,0 +1,648 @@
+/*****************************************************************************
+ * strings.c: String related functions
+ *****************************************************************************
+ * Copyright (C) 2006 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Antoine Cellerier <dionoea at videolan dot org>
+ *          Daniel Stranger <vlc at schmaller dot de>
+ *          Rémi Denis-Courmont <rem # videolan org>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#include <vlc/vlc.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+/* Needed by str_format_time */
+#include <time.h>
+
+/* Needed by str_format_meta */
+#include "vlc_input.h"
+#include "vlc_meta.h"
+#include "vlc_playlist.h"
+#include <vlc/aout.h>
+
+#include "vlc_strings.h"
+#include "vlc_url.h"
+#include "charset.h"
+
+/**
+ * Unescape URI encoded string
+ * \return decoded duplicated string
+ */
+char *unescape_URI_duplicate( const char *psz )
+{
+    char *psz_dup = strdup( psz );
+    unescape_URI( psz_dup );
+    return psz_dup;
+}
+
+/**
+ * Unescape URI encoded string in place
+ * \return nothing
+ */
+void unescape_URI( char *psz )
+{
+    unsigned char *in = (unsigned char *)psz, *out = in, c;
+
+    while( ( c = *in++ ) != '\0' )
+    {
+        switch( c )
+        {
+            case '%':
+            {
+                char val[5], *pval = val;
+                unsigned long cp;
+
+                switch( c = *in++ )
+                {
+                    case '\0':
+                        return;
+
+                    case 'u':
+                    case 'U':
+                        if( ( *pval++ = *in++ ) == '\0' )
+                            return;
+                        if( ( *pval++ = *in++ ) == '\0' )
+                            return;
+                        c = *in++;
+
+                    default:
+                        *pval++ = c;
+                        if( ( *pval++ = *in++ ) == '\0' )
+                            return;
+                        *pval = '\0';
+                }
+
+                cp = strtoul( val, NULL, 0x10 );
+                if( cp < 0x80 )
+                    *out++ = cp;
+                else
+                if( cp < 0x800 )
+                {
+                    *out++ = (( cp >>  6)         | 0xc0);
+                    *out++ = (( cp        & 0x3f) | 0x80);
+                }
+                else
+                {
+                    assert( cp < 0x10000 );
+                    *out++ = (( cp >> 12)         | 0xe0);
+                    *out++ = (((cp >>  6) & 0x3f) | 0x80);
+                    *out++ = (( cp        & 0x3f) | 0x80);
+                }
+                break;
+            }
+
+            /* + is not a special case - it means plus, not space. */
+
+            default:
+                /* Inserting non-ASCII or non-printable characters is unsafe,
+                 * and no sane browser will send these unencoded */
+                if( ( c < 32 ) || ( c > 127 ) )
+                    *out++ = '?';
+                else
+                    *out++ = c;
+        }
+    }
+    *out = '\0';
+}
+
+/**
+ * Decode encoded URI string
+ * \return decoded duplicated string
+ */
+char *decode_URI_duplicate( const char *psz )
+{
+    char *psz_dup = strdup( psz );
+    decode_URI( psz_dup );
+    return psz_dup;
+}
+
+/**
+ * Decode encoded URI string in place
+ * \return nothing
+ */
+void decode_URI( char *psz )
+{
+    unsigned char *in = (unsigned char *)psz, *out = in, c;
+
+    while( ( c = *in++ ) != '\0' )
+    {
+        switch( c )
+        {
+            case '%':
+            {
+                char hex[3];
+
+                if( ( ( hex[0] = *in++ ) == 0 )
+                 || ( ( hex[1] = *in++ ) == 0 ) )
+                    return;
+
+                hex[2] = '\0';
+                *out++ = (unsigned char)strtoul( hex, NULL, 0x10 );
+                break;
+            }
+
+            case '+':
+                *out++ = ' ';
+                break;
+
+            default:
+                /* Inserting non-ASCII or non-printable characters is unsafe,
+                 * and no sane browser will send these unencoded */
+                if( ( c < 32 ) || ( c > 127 ) )
+                    *out++ = '?';
+                else
+                    *out++ = c;
+        }
+    }
+    *out = '\0';
+    EnsureUTF8( psz );
+}
+
+static inline int isurlsafe( int c )
+{
+    return ( (unsigned char)( c - 'a' ) < 26 )
+            || ( (unsigned char)( c - 'A' ) < 26 )
+            || ( (unsigned char)( c - '0' ) < 10 )
+        /* Hmm, we should not encode character that are allowed in URLs
+         * (even if they are not URL-safe), nor URL-safe characters.
+         * We still encode some of them because of Microsoft's crap browser.
+         */
+            || ( strchr( "-_.", c ) != NULL );
+}
+
+static inline char url_hexchar( int c )
+{
+    return ( c < 10 ) ? c + '0' : c + 'A' - 10;
+}
+
+/**
+ * encode_URI_component
+ * Encodes an URI component.
+ *
+ * @param psz_url nul-terminated UTF-8 representation of the component.
+ * Obviously, you can't pass an URI containing a nul character, but you don't
+ * want to do that, do you?
+ *
+ * @return encoded string (must be free()'d)
+ */
+char *encode_URI_component( const char *psz_url )
+{
+    char psz_enc[3 * strlen( psz_url ) + 1], *out = psz_enc;
+    const uint8_t *in;
+
+    for( in = (const uint8_t *)psz_url; *in; in++ )
+    {
+        uint8_t c = *in;
+
+        if( isurlsafe( c ) )
+            *out++ = (char)c;
+        else
+        if ( c == ' ')
+            *out++ = '+';
+        else
+        {
+            *out++ = '%';
+            *out++ = url_hexchar( c >> 4 );
+            *out++ = url_hexchar( c & 0xf );
+        }
+    }
+    *out++ = '\0';
+
+    return strdup( psz_enc );
+}
+
+/**
+ * Converts "&lt;", "&gt;" and "&amp;" to "<", ">" and "&"
+ * \param string to convert
+ */
+void resolve_xml_special_chars( char *psz_value )
+{
+    char *p_pos = psz_value;
+
+    while ( *psz_value )
+    {
+        if( !strncmp( psz_value, "&lt;", 4 ) )
+        {
+            *p_pos = '<';
+            psz_value += 4;
+        }
+        else if( !strncmp( psz_value, "&gt;", 4 ) )
+        {
+            *p_pos = '>';
+            psz_value += 4;
+        }
+        else if( !strncmp( psz_value, "&amp;", 5 ) )
+        {
+            *p_pos = '&';
+            psz_value += 5;
+        }
+        else if( !strncmp( psz_value, "&quot;", 6 ) )
+        {
+            *p_pos = '\"';
+            psz_value += 6;
+        }
+        else if( !strncmp( psz_value, "&#039;", 6 ) )
+        {
+            *p_pos = '\'';
+            psz_value += 6;
+        }
+        else
+        {
+            *p_pos = *psz_value;
+            psz_value++;
+        }
+
+        p_pos++;
+    }
+
+    *p_pos = '\0';
+}
+
+/**
+ * Converts '<', '>', '\"', '\'' and '&' to their html entities
+ * \param psz_content simple element content that is to be converted
+ */
+char *convert_xml_special_chars( const char *psz_content )
+{
+    char *psz_temp = malloc( 6 * strlen( psz_content ) + 1 );
+    const char *p_from = psz_content;
+    char *p_to   = psz_temp;
+
+    while ( *p_from )
+    {
+        if ( *p_from == '<' )
+        {
+            strcpy( p_to, "&lt;" );
+            p_to += 4;
+        }
+        else if ( *p_from == '>' )
+        {
+            strcpy( p_to, "&gt;" );
+            p_to += 4;
+        }
+        else if ( *p_from == '&' )
+        {
+            strcpy( p_to, "&amp;" );
+            p_to += 5;
+        }
+        else if( *p_from == '\"' )
+        {
+            strcpy( p_to, "&quot;" );
+            p_to += 6;
+        }
+        else if( *p_from == '\'' )
+        {
+            strcpy( p_to, "&#039;" );
+            p_to += 6;
+        }
+        else
+        {
+            *p_to = *p_from;
+            p_to++;
+        }
+        p_from++;
+    }
+    *p_to = '\0';
+
+    return psz_temp;
+}
+
+/****************************************************************************
+ * String formating functions
+ ****************************************************************************/
+char *str_format_time(char *tformat )
+{
+    char buffer[255];
+    time_t curtime;
+#if defined(HAVE_LOCALTIME_R)
+    struct tm loctime;
+#else
+    struct tm *loctime;
+#endif
+
+    /* Get the current time.  */
+    curtime = time( NULL );
+
+    /* Convert it to local time representation.  */
+#if defined(HAVE_LOCALTIME_R)
+    localtime_r( &curtime, &loctime );
+    strftime( buffer, 255, tformat, &loctime );
+#else
+    loctime = localtime( &curtime );
+    strftime( buffer, 255, tformat, loctime );
+#endif
+    return strdup( buffer );
+}
+
+#define INSERT_STRING( check, string )                              \
+                    if( check && string )                           \
+                    {                                               \
+                        int len = strlen( string );                 \
+                        dst = realloc( dst,                         \
+                                       i_size = i_size + len + 1 ); \
+                        strncpy( d, string, len+1 );                \
+                        d += len;                                   \
+                    }                                               \
+                    else                                            \
+                    {                                               \
+                        *d = '-';                                   \
+                        d++;                                        \
+                    }
+char *__str_format_meta( vlc_object_t *p_object, char *string )
+{
+    char *s = string;
+    char *dst = malloc( 1000 );
+    char *d = dst;
+    int b_is_format = 0;
+    char buf[10];
+    int i_size = strlen( string );
+
+    playlist_t *p_playlist = pl_Yield( p_object );
+    input_thread_t *p_input = p_playlist->p_input;
+    input_item_t *p_item = NULL;
+    pl_Release( p_object );
+    if( p_input )
+    {
+        vlc_object_yield( p_input );
+        p_item = p_input->input.p_item;
+        if( p_item )
+            vlc_mutex_lock( &p_item->lock );
+    }
+
+    sprintf( dst, string );
+
+    while( *s )
+    {
+        if( b_is_format )
+        {
+            switch( *s )
+            {
+                case 'a':
+                    INSERT_STRING( p_item && p_item->p_meta,
+                                   p_item->p_meta->psz_artist );
+                    break;
+                case 'b':
+                    INSERT_STRING( p_item && p_item->p_meta,
+                                   p_item->p_meta->psz_album );
+                    break;
+                case 'c':
+                    INSERT_STRING( p_item && p_item->p_meta,
+                                   p_item->p_meta->psz_copyright );
+                    break;
+                case 'd':
+                    INSERT_STRING( p_item && p_item->p_meta,
+                                   p_item->p_meta->psz_description );
+                    break;
+                case 'e':
+                    INSERT_STRING( p_item && p_item->p_meta,
+                                   p_item->p_meta->psz_encodedby );
+                    break;
+                case 'g':
+                    INSERT_STRING( p_item && p_item->p_meta,
+                                   p_item->p_meta->psz_genre );
+                    break;
+                case 'l':
+                    INSERT_STRING( p_item && p_item->p_meta,
+                                   p_item->p_meta->psz_language );
+                    break;
+                case 'n':
+                    INSERT_STRING( p_item && p_item->p_meta,
+                                   p_item->p_meta->psz_tracknum );
+                    break;
+                case 'p':
+                    INSERT_STRING( p_item && p_item->p_meta,
+                                   p_item->p_meta->psz_nowplaying );
+                    break;
+                case 'r':
+                    INSERT_STRING( p_item && p_item->p_meta,
+                                   p_item->p_meta->psz_rating );
+                    break;
+                case 's':
+                {
+                    char *lang;
+                    if( p_input )
+                    {
+                        lang = var_GetString( p_input, "sub-language" );
+                    }
+                    else
+                    {
+                        lang = strdup( "-" );
+                    }
+                    INSERT_STRING( 1, lang );
+                    free( lang );
+                    break;
+                }
+                case 't':
+                    INSERT_STRING( p_item && p_item->p_meta,
+                                   p_item->p_meta->psz_title );
+                    break;
+                case 'u':
+                    INSERT_STRING( p_item && p_item->p_meta,
+                                   p_item->p_meta->psz_url );
+                    break;
+                case 'A':
+                    INSERT_STRING( p_item && p_item->p_meta,
+                                   p_item->p_meta->psz_date );
+                    break;
+                case 'B':
+                    if( p_input )
+                    {
+                        snprintf( buf, 10, "%d",
+                                  var_GetInteger( p_input, "bit-rate" )/1000 );
+                    }
+                    else
+                    {
+                        sprintf( buf, "-" );
+                    }
+                    INSERT_STRING( 1, buf );
+                    break;
+                case 'C':
+                    if( p_input )
+                    {
+                        snprintf( buf, 10, "%d",
+                                  var_GetInteger( p_input, "chapter" ) );
+                    }
+                    else
+                    {
+                        sprintf( buf, "-" );
+                    }
+                    INSERT_STRING( 1, buf );
+                    break;
+                case 'D':
+                    if( p_item )
+                    {
+                        sprintf( buf, "%02d:%02d:%02d",
+                                 (int)(p_item->i_duration/(3600000000)),
+                                 (int)((p_item->i_duration/(60000000))%60),
+                                 (int)((p_item->i_duration/1000000)%60) );
+                    }
+                    else
+                    {
+                        sprintf( buf, "--:--:--" );
+                    }
+                    INSERT_STRING( 1, buf );
+                    break;
+                case 'F':
+                    INSERT_STRING( p_item, p_item->psz_uri );
+                    break;
+                case 'I':
+                    if( p_input )
+                    {
+                        snprintf( buf, 10, "%d",
+                                  var_GetInteger( p_input, "title" ) );
+                    }
+                    else
+                    {
+                        sprintf( buf, "-" );
+                    }
+                    INSERT_STRING( 1, buf );
+                    break;
+                case 'L':
+                    if( p_item && p_input )
+                    {
+                        sprintf( buf, "%02d:%02d:%02d",
+                     (int)((p_item->i_duration-p_input->i_time)/(3600000000)),
+                     (int)(((p_item->i_duration-p_input->i_time)/(60000000))%60),
+                     (int)(((p_item->i_duration-p_input->i_time)/1000000)%60) );
+                    }
+                    else
+                    {
+                        sprintf( buf, "--:--:--" );
+                    }
+                    INSERT_STRING( 1, buf );
+                    break;
+                case 'N':
+                    INSERT_STRING( p_item, p_item->psz_name );
+                    break;
+                case 'O':
+                {
+                    char *lang;
+                    if( p_input )
+                    {
+                        lang = var_GetString( p_input, "audio-language" );
+                    }
+                    else
+                    {
+                        lang = strdup( "-" );
+                    }
+                    INSERT_STRING( 1, lang );
+                    free( lang );
+                    break;
+                }
+                case 'P':
+                    if( p_input )
+                    {
+                        snprintf( buf, 10, "%2.1lf",
+                                  var_GetFloat( p_input, "position" ) * 100. );
+                    }
+                    else
+                    {
+                        sprintf( buf, "--.-%%" );
+                    }
+                    INSERT_STRING( 1, buf );
+                    break;
+                case 'R':
+                    if( p_input )
+                    {
+                        int r = var_GetInteger( p_input, "rate" );
+                        snprintf( buf, 10, "%d.%d", r/1000, r%1000 );
+                    }
+                    else
+                    {
+                        sprintf( buf, "-" );
+                    }
+                    INSERT_STRING( 1, buf );
+                    break;
+                case 'S':
+                    if( p_input )
+                    {
+                        int r = var_GetInteger( p_input, "sample-rate" );
+                        snprintf( buf, 10, "%d.%d", r/1000, (r/100)%10 );
+                    }
+                    else
+                    {
+                        sprintf( buf, "-" );
+                    }
+                    INSERT_STRING( 1, buf );
+                    break;
+                case 'T':
+                    if( p_input )
+                    {
+                        sprintf( buf, "%02d:%02d:%02d",
+                                 (int)(p_input->i_time/(3600000000)),
+                                 (int)((p_input->i_time/(60000000))%60),
+                                 (int)((p_input->i_time/1000000)%60) );
+                    }
+                    else
+                    {
+                        sprintf( buf, "--:--:--" );
+                    }
+                    INSERT_STRING( 1, buf );
+                    break;
+                case 'U':
+                    INSERT_STRING( p_item && p_item->p_meta,
+                                   p_item->p_meta->psz_publisher );
+                    break;
+                case 'V':
+                {
+                    audio_volume_t volume;
+                    aout_VolumeGet( p_object, &volume );
+                    snprintf( buf, 10, "%d", volume );
+                    INSERT_STRING( 1, buf );
+                    break;
+                }
+                case '_':
+                    *d = '\n';
+                    d++;
+                    break;
+
+                default:
+                    *d = *s;
+                    d++;
+                    break;
+            }
+            b_is_format = 0;
+        }
+        else if( *s == '$' )
+        {
+            b_is_format = 1;
+        }
+        else
+        {
+            *d = *s;
+            d++;
+        }
+        s++;
+    }
+    *d = '\0';
+
+    if( p_input )
+    {
+        vlc_object_release( p_input );
+        if( p_item )
+            vlc_mutex_unlock( &p_item->lock );
+    }
+
+    return dst;
+}
diff --git a/src/text/unicode.c b/src/text/unicode.c
new file mode 100644 (file)
index 0000000..03b167e
--- /dev/null
@@ -0,0 +1,777 @@
+/*****************************************************************************
+ * unicode.c: Unicode <-> locale functions
+ *****************************************************************************
+ * Copyright (C) 2005-2006 the VideoLAN team
+ * Copyright © 2005-2006 Rémi Denis-Courmont
+ * $Id$
+ *
+ * Authors: Rémi Denis-Courmont <rem # videolan.org>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#include <vlc/vlc.h>
+#include "charset.h"
+
+#include <assert.h>
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#ifdef HAVE_DIRENT_H
+#  include <dirent.h>
+#endif
+#ifdef UNDER_CE
+#  include <tchar.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+#ifdef WIN32
+# include <io.h>
+#else
+# include <unistd.h>
+#endif
+
+#ifndef HAVE_LSTAT
+# define lstat( a, b ) stat(a, b)
+#endif
+
+#ifdef __APPLE__
+/* Define this if the OS always use UTF-8 internally */
+# define ASSUME_UTF8 1
+#endif
+
+#ifndef ASSUME_UTF8
+# if defined (HAVE_ICONV)
+/* libiconv is more powerful than Win32 API (it has translit) */
+#  define USE_ICONV 1
+# elif defined (WIN32) || defined (UNDER_CE)
+#  define USE_MB2MB 1
+# else
+#  error No UTF8 charset conversion implemented on this platform!
+# endif
+#endif
+
+typedef struct locale_data_t
+{
+#if defined (USE_ICONV)
+    vlc_iconv_t hd;
+    vlc_mutex_t lock;
+#elif defined (USE_MB2MB)
+    UINT fromCP;
+    UINT toCP;
+#endif
+} locale_data_t;
+
+static locale_data_t from_locale, to_locale;
+
+
+void LocaleInit( vlc_object_t *p_this )
+{
+#if defined USE_ICONV
+    char *psz_charset;
+
+    if( vlc_current_charset( &psz_charset ) )
+        /* UTF-8 */
+        from_locale.hd = to_locale.hd = (vlc_iconv_t)(-1);
+    else
+    {
+        /* not UTF-8 */
+        char psz_buf[strlen( psz_charset ) + sizeof( "//translit" )];
+        const char *psz_conv;
+
+        /*
+         * Still allow non-ASCII characters when the locale is not set.
+         * Western Europeans are being favored for historical reasons.
+         */
+        if( strcmp( psz_charset, "ASCII" ) )
+        {
+            sprintf( psz_buf, "%s//translit", psz_charset );
+            psz_conv = psz_buf;
+        }
+        else
+            psz_conv = "ISO-8859-1//translit";
+
+        vlc_mutex_init( p_this, &from_locale.lock );
+        vlc_mutex_init( p_this, &to_locale.lock );
+        from_locale.hd = vlc_iconv_open( "UTF-8", psz_conv );
+        to_locale.hd = vlc_iconv_open( psz_conv, "UTF-8" );
+    }
+
+    free( psz_charset );
+
+    assert( (from_locale.hd == (vlc_iconv_t)(-1))
+            == (to_locale.hd == (vlc_iconv_t)(-1)) );
+
+#elif defined (USE_MB2MB)
+    to_locale.toCP = from_locale.fromCP = CP_ACP;
+    from_locale.toCP = to_locale.fromCP = CP_UTF8;
+#else
+    (void)p_this;
+#endif
+}
+
+void LocaleDeinit( void )
+{
+#ifdef USE_ICONV
+    if( to_locale.hd != (vlc_iconv_t)(-1) )
+    {
+        vlc_iconv_close( to_locale.hd );
+        vlc_mutex_destroy( &to_locale.lock );
+    }
+
+    if( from_locale.hd != (vlc_iconv_t)(-1) )
+    {
+        vlc_iconv_close( from_locale.hd );
+        vlc_mutex_destroy( &from_locale.lock );
+    }
+#endif
+}
+
+static char *locale_fast (const char *string, locale_data_t *p)
+{
+#if defined (USE_ICONV)
+    vlc_iconv_t hd = p->hd;
+
+    if (hd == (vlc_iconv_t)(-1))
+        return (char *)string;
+
+    const char *iptr = string;
+    size_t inb = strlen (string);
+    size_t outb = inb * 6 + 1;
+    char output[outb], *optr = output;
+
+    if (string == NULL)
+        return NULL;
+
+    vlc_mutex_lock (&p->lock);
+    vlc_iconv (hd, NULL, NULL, NULL, NULL);
+
+    while (vlc_iconv (hd, &iptr, &inb, &optr, &outb) == (size_t)(-1))
+    {
+        *optr++ = '?';
+        outb--;
+        iptr++;
+        inb--;
+        vlc_iconv (hd, NULL, NULL, NULL, NULL);
+    }
+    vlc_mutex_unlock (&p->lock);
+    *optr = '\0';
+
+    assert (inb == 0);
+    assert (*iptr == '\0');
+    assert (*optr == '\0');
+    assert (strlen (output) == (size_t)(optr - output));
+    return strdup (output);
+#elif defined (USE_MB2MB)
+    char *out;
+    wchar_t *wide;
+    int len;
+
+    if (string == NULL)
+        return NULL;
+
+    len = MultiByteToWideChar (p->fromCP, 0, string, -1, NULL, 0);
+    if (len == 0)
+        return NULL;
+
+    wchar_t wide[len];
+
+    MultiByteToWideChar (p->fromCP, 0, string, -1, wide, len);
+    len = WideCharToMultiByte (p->toCP, 0, wide, -1, NULL, 0, NULL, NULL);
+    if (len == 0)
+        return NULL;
+    out = malloc (len);
+
+    WideCharToMultiByte (p->toCP, 0, wide, -1, out, len, NULL, NULL);
+    return out;
+#else
+    return (char *)string;
+#endif
+}
+
+
+static inline char *locale_dup (const char *string, locale_data_t *p)
+{
+#if defined (USE_ICONV)
+    return (p->hd == (vlc_iconv_t)(-1))
+            ? strdup (string)
+            : locale_fast (string, p);
+#elif defined (USE_MB2MB)
+    return locale_fast (string, p);
+#else
+    return strdup (string);
+#endif
+}
+
+
+void LocaleFree (const char *str)
+{
+#if defined (USE_ICONV)
+    assert ((to_locale.hd == (vlc_iconv_t)(-1))
+         == (from_locale.hd == (vlc_iconv_t)(-1)));
+
+    if( to_locale.hd != (vlc_iconv_t)(-1) )
+        free ((char *)str);
+#elif defined (USE_MB2MB)
+    free ((char *)str);
+#endif
+}
+
+
+/**
+ * FromLocale: converts a locale string to UTF-8
+ *
+ * @param locale nul-terminated string to be converted
+ *
+ * @return a nul-terminated UTF-8 string, or NULL in case of error.
+ * To avoid memory leak, you have to pass the result to LocaleFree()
+ * when it is no longer needed.
+ */
+char *FromLocale (const char *locale)
+{
+    return locale_fast (locale, &from_locale);
+}
+
+char *FromLocaleDup (const char *locale)
+{
+    return locale_dup (locale, &from_locale);
+}
+
+
+/**
+ * ToLocale: converts a UTF-8 string to local system encoding.
+ *
+ * @param utf8 nul-terminated string to be converted
+ *
+ * @return a nul-terminated string, or NULL in case of error.
+ * To avoid memory leak, you have to pass the result to LocaleFree()
+ * when it is no longer needed.
+ */
+char *ToLocale (const char *utf8)
+{
+    return locale_fast (utf8, &to_locale);
+}
+
+
+static char *ToLocaleDup (const char *utf8)
+{
+    return locale_dup (utf8, &to_locale);
+}
+
+
+/**
+ * utf8_open: open() wrapper for UTF-8 filenames
+ */
+int utf8_open (const char *filename, int flags, mode_t mode)
+{
+#if defined (WIN32) || defined (UNDER_CE)
+    if (GetVersion() < 0x80000000)
+    {
+        /* for Windows NT and above */
+        wchar_t wpath[MAX_PATH + 1];
+
+        if (!MultiByteToWideChar (CP_UTF8, 0, filename, -1, wpath, MAX_PATH))
+        {
+            errno = ENOENT;
+            return -1;
+        }
+        wpath[MAX_PATH] = L'\0';
+
+        /*
+         * open() cannot open files with non-“ANSI” characters on Windows.
+         * We use _wopen() instead. Same thing for mkdir() and stat().
+         */
+        return _wopen (wpath, flags, mode);
+    }
+#endif
+    const char *local_name = ToLocale (filename);
+
+    if (local_name == NULL)
+    {
+        errno = ENOENT;
+        return -1;
+    }
+
+    int fd = open (local_name, flags, mode);
+    LocaleFree (local_name);
+    return fd;
+}
+
+/**
+ * utf8_fopen: fopen() wrapper for UTF-8 filenames
+ */
+FILE *utf8_fopen (const char *filename, const char *mode)
+{
+    int rwflags = 0, oflags = 0;
+    vlc_bool_t append = VLC_FALSE;
+
+    for (const char *ptr = mode; *ptr; ptr++)
+    {
+        switch (*ptr)
+        {
+            case 'r':
+                rwflags = O_RDONLY;
+                break;
+
+            case 'a':
+                rwflags = O_WRONLY;
+                oflags |= O_CREAT;
+                append = VLC_TRUE;
+                break;
+
+            case 'w':
+                rwflags = O_WRONLY;
+                oflags |= O_CREAT | O_TRUNC;
+                break;
+
+            case '+':
+                rwflags = O_RDWR;
+                break;
+        }
+    }
+
+    int fd = utf8_open (filename, rwflags | oflags, 0666);
+    if (fd == -1)
+        return NULL;
+
+    if (append && (lseek (fd, 0, SEEK_END) == -1))
+    {
+        close (fd);
+        return NULL;
+    }
+
+    FILE *stream = fdopen (fd, mode);
+    if (stream == NULL)
+        close (fd);
+
+    return stream;
+}
+
+/**
+ * utf8_mkdir: Calls mkdir() after conversion of file name to OS locale
+ */
+int utf8_mkdir( const char *dirname )
+{
+#if defined (UNDER_CE) || defined (WIN32)
+    wchar_t wname[MAX_PATH + 1];
+    char mod[MAX_PATH + 1];
+    int i;
+
+    /* Convert '/' into '\' */
+    for( i = 0; *dirname; i++ )
+    {
+        if( i == MAX_PATH )
+            return -1; /* overflow */
+
+        if( *dirname == '/' )
+            mod[i] = '\\';
+        else
+            mod[i] = *dirname;
+        dirname++;
+
+    }
+    mod[i] = 0;
+
+    if( MultiByteToWideChar( CP_UTF8, 0, mod, -1, wname, MAX_PATH ) == 0 )
+    {
+        errno = ENOENT;
+        return -1;
+    }
+    wname[MAX_PATH] = L'\0';
+
+    if( CreateDirectoryW( wname, NULL ) == 0 )
+    {
+        if( GetLastError( ) == ERROR_ALREADY_EXISTS )
+            errno = EEXIST;
+        else
+            errno = ENOENT;
+        return -1;
+    }
+    return 0;
+#else
+    char *locname = ToLocale( dirname );
+    int res;
+
+    if( locname == NULL )
+    {
+        errno = ENOENT;
+        return -1;
+    }
+    res = mkdir( locname, 0755 );
+
+    LocaleFree( locname );
+    return res;
+#endif
+}
+
+
+DIR *utf8_opendir( const char *dirname )
+{
+    /* TODO: support for WinNT non-ACP filenames */
+    const char *local_name = ToLocale( dirname );
+
+    if( local_name != NULL )
+    {
+        DIR *dir = vlc_opendir_wrapper( local_name );
+        LocaleFree( local_name );
+        return dir;
+    }
+    else
+        errno = ENOENT;
+    return NULL;
+}
+
+
+char *utf8_readdir( void *dir )
+{
+    struct dirent *ent;
+
+    ent = vlc_readdir_wrapper( (DIR *)dir );
+    if( ent == NULL )
+        return NULL;
+
+    return vlc_fix_readdir( ent->d_name );
+}
+
+static int dummy_select( const char *str )
+{
+    (void)str;
+    return 1;
+}
+
+int utf8_scandir( const char *dirname, char ***namelist,
+                  int (*select)( const char * ),
+                  int (*compar)( const char **, const char ** ) )
+{
+    DIR *dir = utf8_opendir( dirname );
+
+    if( select == NULL )
+        select = dummy_select;
+
+    if( dir == NULL )
+        return -1;
+    else
+    {
+        char **tab = NULL;
+        char *entry;
+        unsigned num = 0;
+
+        while( ( entry = utf8_readdir( dir ) ) != NULL )
+        {
+            char **newtab;
+
+            if( !select( entry ) )
+            {
+                free( entry );
+                continue;
+            }
+
+            newtab = realloc( tab, sizeof( char * ) * (num + 1) );
+            if( newtab == NULL )
+            {
+                free( entry );
+                goto error;
+            }
+            tab = newtab;
+            tab[num++] = entry;
+        }
+        vlc_closedir_wrapper( dir );
+
+        if( compar != NULL )
+            qsort( tab, num, sizeof( tab[0] ),
+                   (int (*)( const void *, const void *))compar );
+
+        *namelist = tab;
+        return num;
+
+    error:{
+        unsigned i;
+
+        for( i = 0; i < num; i++ )
+            free( tab[i] );
+        if( tab != NULL )
+            free( tab );
+        return -1;}
+    }
+}
+
+
+static int utf8_statEx( const char *filename, struct stat *buf,
+                        vlc_bool_t deref )
+{
+#if defined (WIN32) || defined (UNDER_CE)
+    /* retrieve Windows OS version */
+    if( GetVersion() < 0x80000000 )
+    {
+        /* for Windows NT and above */
+        wchar_t wpath[MAX_PATH + 1];
+
+        if( !MultiByteToWideChar( CP_UTF8, 0, filename, -1, wpath, MAX_PATH ) )
+        {
+            errno = ENOENT;
+            return -1;
+        }
+        wpath[MAX_PATH] = L'\0';
+
+        return _wstati64( wpath, buf );
+    }
+#endif
+#ifdef HAVE_SYS_STAT_H
+    const char *local_name = ToLocale( filename );
+
+    if( local_name != NULL )
+    {
+        int res = deref ? stat( local_name, buf )
+                       : lstat( local_name, buf );
+        LocaleFree( local_name );
+        return res;
+    }
+    errno = ENOENT;
+#endif
+    return -1;
+}
+
+
+int utf8_stat( const char *filename, struct stat *buf)
+{
+    return utf8_statEx( filename, buf, VLC_TRUE );
+}
+
+int utf8_lstat( const char *filename, struct stat *buf)
+{
+    return utf8_statEx( filename, buf, VLC_FALSE );
+}
+
+/**
+ * utf8_*printf: *printf with conversion from UTF-8 to local encoding
+ */
+static int utf8_vasprintf( char **str, const char *fmt, va_list ap )
+{
+    char *utf8;
+    int res = vasprintf( &utf8, fmt, ap );
+    if( res == -1 )
+        return -1;
+
+    *str = ToLocaleDup( utf8 );
+    free( utf8 );
+    return res;
+}
+
+int utf8_vfprintf( FILE *stream, const char *fmt, va_list ap )
+{
+    char *str;
+    int res = utf8_vasprintf( &str, fmt, ap );
+    if( res == -1 )
+        return -1;
+
+    fputs( str, stream );
+    free( str );
+    return res;
+}
+
+int utf8_fprintf( FILE *stream, const char *fmt, ... )
+{
+    va_list ap;
+    int res;
+
+    va_start( ap, fmt );
+    res = utf8_vfprintf( stream, fmt, ap );
+    va_end( ap );
+    return res;
+}
+
+
+static char *CheckUTF8( char *str, char rep )
+#define isutf8cont( c ) (((c) >= 0x80) && ((c) <= 0xBF)) 
+{
+    unsigned char *ptr, c;
+
+    assert (str != NULL);
+
+    ptr = (unsigned char *)str;
+    while( (c = *ptr) != '\0' )
+    {
+        /* US-ASCII, 1 byte */
+        if( c <= 0x7F )
+            ptr++; /* OK */
+        else
+        /* 2 bytes */
+        if( ( c >= 0xC2 ) && ( c <= 0xDF ) )
+        {
+            c = ptr[1];
+            if( isutf8cont( c ) )
+                ptr += 2; /* OK */
+            else
+                goto error;
+        }
+        else
+        /* 3 bytes */
+        if( c == 0xE0 )
+        {
+            c = ptr[1];
+            if( ( c >= 0xA0 ) && ( c <= 0xBF ) )
+            {
+                c = ptr[2];
+                if( isutf8cont( c ) )
+                    ptr += 3; /* OK */
+                else
+                    goto error;
+            }
+            else
+                goto error;
+        }
+        else
+        if( ( ( c >= 0xE1 ) && ( c <= 0xEC ) ) || ( c == 0xEC )
+         || ( c == 0xEE ) || ( c == 0xEF ) )
+        {
+            c = ptr[1];
+            if( isutf8cont( c ) )
+            {
+                c = ptr[2];
+                if( isutf8cont( c ) )
+                    ptr += 3; /* OK */
+                else
+                    goto error;
+            }
+            else
+                goto error;
+        }
+        else
+        if( c == 0xED )
+        {
+            c = ptr[1];
+            if( ( c >= 0x80 ) && ( c <= 0x9F ) )
+            {
+                c = ptr[2];
+                if( isutf8cont( c ) )
+                    ptr += 3; /* OK */
+                else
+                    goto error;
+            }
+            else
+                goto error;
+        }
+        else
+        /* 4 bytes */
+        if( c == 0xF0 )
+        {
+            c = ptr[1];
+            if( ( c >= 0x90 ) && ( c <= 0xBF ) )
+            {
+                c = ptr[2];
+                if( isutf8cont( c ) )
+                {
+                    c = ptr[3];
+                    if( isutf8cont( c ) )
+                        ptr += 4; /* OK */
+                    else
+                        goto error;
+                }
+                else
+                    goto error;
+            }
+            else
+                goto error;
+        }
+        else
+        if( ( c >= 0xF1 ) && ( c <= 0xF3 ) )
+        {
+            c = ptr[1];
+            if( isutf8cont( c ) )
+            {
+                c = ptr[2];
+                if( isutf8cont( c ) )
+                {
+                    c = ptr[3];
+                    if( isutf8cont( c ) )
+                        ptr += 4; /* OK */
+                    goto error;
+                }
+                else
+                    goto error;
+            }
+            else
+                goto error;
+        }
+        else
+        if( c == 0xF4 )
+        {
+            c = ptr[1];
+            if( ( c >= 0x80 ) && ( c <= 0x8F ) )
+            {
+                c = ptr[2];
+                if( isutf8cont( c ) )
+                {
+                    c = ptr[3];
+                    if( isutf8cont( c ) )
+                        ptr += 4; /* OK */
+                    else
+                        goto error;
+                }
+                else
+                    goto error;
+            }
+            else
+                goto error;
+        }
+        else
+            goto error;
+
+        continue;
+
+error:
+        if( rep == 0 )
+            return NULL;
+        *ptr++ = '?';
+        str = NULL;
+    }
+
+    return str;
+}
+
+/**
+ * EnsureUTF8: replaces invalid/overlong UTF-8 sequences with question marks
+ * Note that it is not possible to convert from Latin-1 to UTF-8 on the fly,
+ * so we don't try that, even though it would be less disruptive.
+ *
+ * @return str if it was valid UTF-8, NULL if not.
+ */
+char *EnsureUTF8( char *str )
+{
+    return CheckUTF8( str, '?' );
+}
+
+
+/**
+ * IsUTF8: checks whether a string is a valid UTF-8 byte sequence.
+ *
+ * @param str nul-terminated string to be checked
+ *
+ * @return str if it was valid UTF-8, NULL if not.
+ */
+const char *IsUTF8( const char *str )
+{
+    return CheckUTF8( (char *)str, 0 );
+}