/* Win32 internationalization functions.
   Copyright (C) 2000, 2001, 2002, 2004, 2010 Ben Wing.
   Copyright (C) 2000 IKEYAMA Tomonori.

This file is part of XEmacs.

XEmacs 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 3 of the License, or (at your
option) any later version.

XEmacs 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 XEmacs.  If not, see <http://www.gnu.org/licenses/>. */

/* Synched up with: Not in FSF. */

/* Authorship:

   Current primary author: Ben Wing <ben@xemacs.org>

   Created summer 2000 by Ben Wing.  Almost completely written by Ben Wing.
   Little bits of code in some of the Lisp primitives from FSF Emacs.
   Versions of wcscpy, wcsncpy from Cygwin newlib.

   Coding systems written by Ben Wing in file-coding.c; moved here Sep 2001.
   */

#include <config.h>
#include "lisp.h"

#include "elhash.h"
#include "faces.h"
#include "file-coding.h"
#include "frame-impl.h"
#include "window-impl.h"

#include "console-msw-impl.h"
#include "fontcolor-msw-impl.h"

#ifndef CYGWIN_HEADERS
# include <mbctype.h>
#elif defined (MINGW)
int _setmbcp (int);
int _getmbcp (void);
#else
# define NO_EXT_MULTIBYTE_FEATURES
#endif



/*

Detailed info on Microsoft Windows-Related Multilingual Issues:

  (Info-goto-node "(internals)Microsoft Windows-Related Multilingual Issues")
*/

Lisp_Object Qmswindows_multibyte, Qmswindows_multibyte_to_unicode;
Lisp_Object Qmswindows_tstr, Qmswindows_unicode;
Lisp_Object Qmswindows_multibyte_system_default;

Lisp_Object Qansi, Qoem, Qmac, Qebcdic;
/* Qcode_page, Qlocale, Qcurrent, Quser_default, Qsystem_default in
   general-slots.h */

#ifdef MULE

static Lisp_Object Vmswindows_charset_code_page_table;
static Lisp_Object Vmswindows_charset_registry_table;

LCID current_locale;


/************************************************************************/
/*            Language/locale/code page conversion functions            */
/************************************************************************/

struct lang_to_string
{
  int code;
  const Ascbyte *string;
};

struct lang_to_string lang_to_string_table[] =
{
  /* These names change from version to version of VC++, so it's easiest
     just to bracket them all with ifdefs. */
#ifdef LANG_AFRIKAANS
  { LANG_AFRIKAANS, "AFRIKAANS" },
#endif
#ifdef LANG_ALBANIAN
  { LANG_ALBANIAN, "ALBANIAN" },
#endif
#ifdef LANG_ARABIC
  { LANG_ARABIC, "ARABIC" },
#endif
#ifdef LANG_ARMENIAN
  { LANG_ARMENIAN, "ARMENIAN" },
#endif
#ifdef LANG_ASSAMESE
  { LANG_ASSAMESE, "ASSAMESE" },
#endif
#ifdef LANG_AZERI
  { LANG_AZERI, "AZERI" },
#endif
#ifdef LANG_BASQUE
  { LANG_BASQUE, "BASQUE" },
#endif
#ifdef LANG_BELARUSIAN
  { LANG_BELARUSIAN, "BELARUSIAN" },
#endif
#ifdef LANG_BENGALI
  { LANG_BENGALI, "BENGALI" },
#endif
#ifdef LANG_BULGARIAN
  { LANG_BULGARIAN, "BULGARIAN" },
#endif
#ifdef LANG_CATALAN
  { LANG_CATALAN, "CATALAN" },
#endif
#ifdef LANG_CHINESE
  { LANG_CHINESE, "CHINESE" },
#endif
#ifdef LANG_CROATIAN
  { LANG_CROATIAN, "CROATIAN" },
#endif
#ifdef LANG_CZECH
  { LANG_CZECH, "CZECH" },
#endif
#ifdef LANG_DANISH
  { LANG_DANISH, "DANISH" },
#endif
#ifdef LANG_DUTCH
  { LANG_DUTCH, "DUTCH" },
#endif
#ifdef LANG_ENGLISH
  { LANG_ENGLISH, "ENGLISH" },
#endif
#ifdef LANG_ESTONIAN
  { LANG_ESTONIAN, "ESTONIAN" },
#endif
#ifdef LANG_FAEROESE
  { LANG_FAEROESE, "FAEROESE" },
#endif
#ifdef LANG_FARSI
  { LANG_FARSI, "FARSI" },
#endif
#ifdef LANG_FINNISH
  { LANG_FINNISH, "FINNISH" },
#endif
#ifdef LANG_FRENCH
  { LANG_FRENCH, "FRENCH" },
#endif
#ifdef LANG_GEORGIAN
  { LANG_GEORGIAN, "GEORGIAN" },
#endif
#ifdef LANG_GERMAN
  { LANG_GERMAN, "GERMAN" },
#endif
#ifdef LANG_GREEK
  { LANG_GREEK, "GREEK" },
#endif
#ifdef LANG_GUJARATI
  { LANG_GUJARATI, "GUJARATI" },
#endif
#ifdef LANG_HEBREW
  { LANG_HEBREW, "HEBREW" },
#endif
#ifdef LANG_HINDI
  { LANG_HINDI, "HINDI" },
#endif
#ifdef LANG_HUNGARIAN
  { LANG_HUNGARIAN, "HUNGARIAN" },
#endif
#ifdef LANG_ICELANDIC
  { LANG_ICELANDIC, "ICELANDIC" },
#endif
#ifdef LANG_INDONESIAN
  { LANG_INDONESIAN, "INDONESIAN" },
#endif
#ifdef LANG_ITALIAN
  { LANG_ITALIAN, "ITALIAN" },
#endif
#ifdef LANG_JAPANESE
  { LANG_JAPANESE, "JAPANESE" },
#endif
#ifdef LANG_KANNADA
  { LANG_KANNADA, "KANNADA" },
#endif
#ifdef LANG_KASHMIRI
  { LANG_KASHMIRI, "KASHMIRI" },
#endif
#ifdef LANG_KAZAK
  { LANG_KAZAK, "KAZAK" },
#endif
#ifdef LANG_KONKANI
  { LANG_KONKANI, "KONKANI" },
#endif
#ifdef LANG_KOREAN
  { LANG_KOREAN, "KOREAN" },
#endif
#ifdef LANG_LATVIAN
  { LANG_LATVIAN, "LATVIAN" },
#endif
#ifdef LANG_LITHUANIAN
  { LANG_LITHUANIAN, "LITHUANIAN" },
#endif
#ifdef LANG_MACEDONIAN
  { LANG_MACEDONIAN, "MACEDONIAN" },
#endif
#ifdef LANG_MALAY
  { LANG_MALAY, "MALAY" },
#endif
#ifdef LANG_MALAYALAM
  { LANG_MALAYALAM, "MALAYALAM" },
#endif
#ifdef LANG_MANIPURI
  { LANG_MANIPURI, "MANIPURI" },
#endif
#ifdef LANG_MARATHI
  { LANG_MARATHI, "MARATHI" },
#endif
#ifdef LANG_NEPALI
  { LANG_NEPALI, "NEPALI" },
#endif
#ifdef LANG_NEUTRAL
  { LANG_NEUTRAL, "NEUTRAL" },
#endif
#ifdef LANG_NORWEGIAN
  { LANG_NORWEGIAN, "NORWEGIAN" },
#endif
#ifdef LANG_ORIYA
  { LANG_ORIYA, "ORIYA" },
#endif
#ifdef LANG_POLISH
  { LANG_POLISH, "POLISH" },
#endif
#ifdef LANG_PORTUGUESE
  { LANG_PORTUGUESE, "PORTUGUESE" },
#endif
#ifdef LANG_PUNJABI
  { LANG_PUNJABI, "PUNJABI" },
#endif
#ifdef LANG_ROMANIAN
  { LANG_ROMANIAN, "ROMANIAN" },
#endif
#ifdef LANG_RUSSIAN
  { LANG_RUSSIAN, "RUSSIAN" },
#endif
#ifdef LANG_SANSKRIT
  { LANG_SANSKRIT, "SANSKRIT" },
#endif
#ifdef LANG_SERBIAN
  { LANG_SERBIAN, "SERBIAN" },
#endif
#ifdef LANG_SINDHI
  { LANG_SINDHI, "SINDHI" },
#endif
#ifdef LANG_SLOVAK
  { LANG_SLOVAK, "SLOVAK" },
#endif
#ifdef LANG_SLOVENIAN
  { LANG_SLOVENIAN, "SLOVENIAN" },
#endif
#ifdef LANG_SPANISH
  { LANG_SPANISH, "SPANISH" },
#endif
#ifdef LANG_SWAHILI
  { LANG_SWAHILI, "SWAHILI" },
#endif
#ifdef LANG_SWEDISH
  { LANG_SWEDISH, "SWEDISH" },
#endif
#ifdef LANG_TAMIL
  { LANG_TAMIL, "TAMIL" },
#endif
#ifdef LANG_TATAR
  { LANG_TATAR, "TATAR" },
#endif
#ifdef LANG_TELUGU
  { LANG_TELUGU, "TELUGU" },
#endif
#ifdef LANG_THAI
  { LANG_THAI, "THAI" },
#endif
#ifdef LANG_TURKISH
  { LANG_TURKISH, "TURKISH" },
#endif
#ifdef LANG_UKRAINIAN
  { LANG_UKRAINIAN, "UKRAINIAN" },
#endif
#ifdef LANG_URDU
  { LANG_URDU, "URDU" },
#endif
#ifdef LANG_UZBEK
  { LANG_UZBEK, "UZBEK" },
#endif
#ifdef LANG_VIETNAMESE
  { LANG_VIETNAMESE, "VIETNAMESE" },
#endif
};

struct lang_to_string sublang_to_string_table[] =
{
  { LANG_ARABIC, 0 },
#ifdef SUBLANG_ARABIC_ALGERIA
  { SUBLANG_ARABIC_ALGERIA, "ARABIC_ALGERIA" },
#endif
#ifdef SUBLANG_ARABIC_BAHRAIN
  { SUBLANG_ARABIC_BAHRAIN, "ARABIC_BAHRAIN" },
#endif
#ifdef SUBLANG_ARABIC_EGYPT
  { SUBLANG_ARABIC_EGYPT, "ARABIC_EGYPT" },
#endif
#ifdef SUBLANG_ARABIC_IRAQ
  { SUBLANG_ARABIC_IRAQ, "ARABIC_IRAQ" },
#endif
#ifdef SUBLANG_ARABIC_JORDAN
  { SUBLANG_ARABIC_JORDAN, "ARABIC_JORDAN" },
#endif
#ifdef SUBLANG_ARABIC_KUWAIT
  { SUBLANG_ARABIC_KUWAIT, "ARABIC_KUWAIT" },
#endif
#ifdef SUBLANG_ARABIC_LEBANON
  { SUBLANG_ARABIC_LEBANON, "ARABIC_LEBANON" },
#endif
#ifdef SUBLANG_ARABIC_LIBYA
  { SUBLANG_ARABIC_LIBYA, "ARABIC_LIBYA" },
#endif
#ifdef SUBLANG_ARABIC_MOROCCO
  { SUBLANG_ARABIC_MOROCCO, "ARABIC_MOROCCO" },
#endif
#ifdef SUBLANG_ARABIC_OMAN
  { SUBLANG_ARABIC_OMAN, "ARABIC_OMAN" },
#endif
#ifdef SUBLANG_ARABIC_QATAR
  { SUBLANG_ARABIC_QATAR, "ARABIC_QATAR" },
#endif
#ifdef SUBLANG_ARABIC_SAUDI_ARABIA
  { SUBLANG_ARABIC_SAUDI_ARABIA, "ARABIC_SAUDI_ARABIA" },
#endif
#ifdef SUBLANG_ARABIC_SYRIA
  { SUBLANG_ARABIC_SYRIA, "ARABIC_SYRIA" },
#endif
#ifdef SUBLANG_ARABIC_TUNISIA
  { SUBLANG_ARABIC_TUNISIA, "ARABIC_TUNISIA" },
#endif
#ifdef SUBLANG_ARABIC_UAE
  { SUBLANG_ARABIC_UAE, "ARABIC_UAE" },
#endif
#ifdef SUBLANG_ARABIC_YEMEN
  { SUBLANG_ARABIC_YEMEN, "ARABIC_YEMEN" },
#endif
  { LANG_AZERI, 0 },
#ifdef SUBLANG_AZERI_CYRILLIC
  { SUBLANG_AZERI_CYRILLIC, "AZERI_CYRILLIC" },
#endif
#ifdef SUBLANG_AZERI_LATIN
  { SUBLANG_AZERI_LATIN, "AZERI_LATIN" },
#endif
  { LANG_CHINESE, 0 },
#ifdef SUBLANG_CHINESE_HONGKONG
  { SUBLANG_CHINESE_HONGKONG, "CHINESE_HONGKONG" },
#endif
#ifdef SUBLANG_CHINESE_MACAU
  { SUBLANG_CHINESE_MACAU, "CHINESE_MACAU" },
#endif
#ifdef SUBLANG_CHINESE_SIMPLIFIED
  { SUBLANG_CHINESE_SIMPLIFIED, "CHINESE_SIMPLIFIED" },
#endif
#ifdef SUBLANG_CHINESE_SINGAPORE
  { SUBLANG_CHINESE_SINGAPORE, "CHINESE_SINGAPORE" },
#endif
#ifdef SUBLANG_CHINESE_TRADITIONAL
  { SUBLANG_CHINESE_TRADITIONAL, "CHINESE_TRADITIONAL" },
#endif
  { LANG_DUTCH, 0 },
#ifdef SUBLANG_DUTCH
  { SUBLANG_DUTCH, "DUTCH" },
#endif
#ifdef SUBLANG_DUTCH_BELGIAN
  { SUBLANG_DUTCH_BELGIAN, "DUTCH_BELGIAN" },
#endif
  { LANG_ENGLISH, 0 },
#ifdef SUBLANG_ENGLISH_AUS
  { SUBLANG_ENGLISH_AUS, "ENGLISH_AUS" },
#endif
#ifdef SUBLANG_ENGLISH_BELIZE
  { SUBLANG_ENGLISH_BELIZE, "ENGLISH_BELIZE" },
#endif
#ifdef SUBLANG_ENGLISH_CAN
  { SUBLANG_ENGLISH_CAN, "ENGLISH_CAN" },
#endif
#ifdef SUBLANG_ENGLISH_CARIBBEAN
  { SUBLANG_ENGLISH_CARIBBEAN, "ENGLISH_CARIBBEAN" },
#endif
#ifdef SUBLANG_ENGLISH_EIRE
  { SUBLANG_ENGLISH_EIRE, "ENGLISH_EIRE" },
#endif
#ifdef SUBLANG_ENGLISH_JAMAICA
  { SUBLANG_ENGLISH_JAMAICA, "ENGLISH_JAMAICA" },
#endif
#ifdef SUBLANG_ENGLISH_NZ
  { SUBLANG_ENGLISH_NZ, "ENGLISH_NZ" },
#endif
#ifdef SUBLANG_ENGLISH_PHILIPPINES
  { SUBLANG_ENGLISH_PHILIPPINES, "ENGLISH_PHILIPPINES" },
#endif
#ifdef SUBLANG_ENGLISH_SOUTH_AFRICA
  { SUBLANG_ENGLISH_SOUTH_AFRICA, "ENGLISH_SOUTH_AFRICA" },
#endif
#ifdef SUBLANG_ENGLISH_TRINIDAD
  { SUBLANG_ENGLISH_TRINIDAD, "ENGLISH_TRINIDAD" },
#endif
#ifdef SUBLANG_ENGLISH_UK
  { SUBLANG_ENGLISH_UK, "ENGLISH_UK" },
#endif
#ifdef SUBLANG_ENGLISH_US
  { SUBLANG_ENGLISH_US, "ENGLISH_US" },
#endif
#ifdef SUBLANG_ENGLISH_ZIMBABWE
  { SUBLANG_ENGLISH_ZIMBABWE, "ENGLISH_ZIMBABWE" },
#endif
  { LANG_FRENCH, 0 },
#ifdef SUBLANG_FRENCH
  { SUBLANG_FRENCH, "FRENCH" },
#endif
#ifdef SUBLANG_FRENCH_BELGIAN
  { SUBLANG_FRENCH_BELGIAN, "FRENCH_BELGIAN" },
#endif
#ifdef SUBLANG_FRENCH_CANADIAN
  { SUBLANG_FRENCH_CANADIAN, "FRENCH_CANADIAN" },
#endif
#ifdef SUBLANG_FRENCH_LUXEMBOURG
  { SUBLANG_FRENCH_LUXEMBOURG, "FRENCH_LUXEMBOURG" },
#endif
#ifdef SUBLANG_FRENCH_MONACO
  { SUBLANG_FRENCH_MONACO, "FRENCH_MONACO" },
#endif
#ifdef SUBLANG_FRENCH_SWISS
  { SUBLANG_FRENCH_SWISS, "FRENCH_SWISS" },
#endif
  { LANG_GERMAN, 0 },
#ifdef SUBLANG_GERMAN
  { SUBLANG_GERMAN, "GERMAN" },
#endif
#ifdef SUBLANG_GERMAN_AUSTRIAN
  { SUBLANG_GERMAN_AUSTRIAN, "GERMAN_AUSTRIAN" },
#endif
#ifdef SUBLANG_GERMAN_LIECHTENSTEIN
  { SUBLANG_GERMAN_LIECHTENSTEIN, "GERMAN_LIECHTENSTEIN" },
#endif
#ifdef SUBLANG_GERMAN_LUXEMBOURG
  { SUBLANG_GERMAN_LUXEMBOURG, "GERMAN_LUXEMBOURG" },
#endif
#ifdef SUBLANG_GERMAN_SWISS
  { SUBLANG_GERMAN_SWISS, "GERMAN_SWISS" },
#endif
  { LANG_ITALIAN, 0 },
#ifdef SUBLANG_ITALIAN
  { SUBLANG_ITALIAN, "ITALIAN" },
#endif
#ifdef SUBLANG_ITALIAN_SWISS
  { SUBLANG_ITALIAN_SWISS, "ITALIAN_SWISS" },
#endif
  { LANG_KASHMIRI, 0 },
#ifdef SUBLANG_KASHMIRI_INDIA
  { SUBLANG_KASHMIRI_INDIA, "KASHMIRI_INDIA" },
#endif
  { LANG_KOREAN, 0 },
#ifdef SUBLANG_KOREAN
  { SUBLANG_KOREAN, "KOREAN" },
#endif
#ifdef SUBLANG_KOREAN_JOHAB
  /* NOTE: Omitted in more recent versions of VC++ (e.g. v6.0) */
  { SUBLANG_KOREAN_JOHAB, "KOREAN_JOHAB" },
#endif
  { LANG_LITHUANIAN, 0 },
#ifdef SUBLANG_LITHUANIAN
  { SUBLANG_LITHUANIAN, "LITHUANIAN" },
#endif
#ifdef SUBLANG_LITHUANIAN_CLASSIC
  { SUBLANG_LITHUANIAN_CLASSIC, "LITHUANIAN_CLASSIC" },
#endif
  { LANG_MALAY, 0 },
#ifdef SUBLANG_MALAY_BRUNEI_DARUSSALAM
  { SUBLANG_MALAY_BRUNEI_DARUSSALAM, "MALAY_BRUNEI_DARUSSALAM" },
#endif
#ifdef SUBLANG_MALAY_MALAYSIA
  { SUBLANG_MALAY_MALAYSIA, "MALAY_MALAYSIA" },
#endif
  { LANG_NEPALI, 0 },
#ifdef SUBLANG_NEPALI_INDIA
  { SUBLANG_NEPALI_INDIA, "NEPALI_INDIA" },
#endif
  { LANG_NEUTRAL, 0 },
#ifdef SUBLANG_NEUTRAL
  { SUBLANG_NEUTRAL, "NEUTRAL" },
#endif
  { LANG_NORWEGIAN, 0 },
#ifdef SUBLANG_NORWEGIAN_BOKMAL
  { SUBLANG_NORWEGIAN_BOKMAL, "NORWEGIAN_BOKMAL" },
#endif
#ifdef SUBLANG_NORWEGIAN_NYNORSK
  { SUBLANG_NORWEGIAN_NYNORSK, "NORWEGIAN_NYNORSK" },
#endif
  { LANG_PORTUGUESE, 0 },
#ifdef SUBLANG_PORTUGUESE
  { SUBLANG_PORTUGUESE, "PORTUGUESE" },
#endif
#ifdef SUBLANG_PORTUGUESE_BRAZILIAN
  { SUBLANG_PORTUGUESE_BRAZILIAN, "PORTUGUESE_BRAZILIAN" },
#endif
  { LANG_SERBIAN, 0 },
#ifdef SUBLANG_SERBIAN_CYRILLIC
  { SUBLANG_SERBIAN_CYRILLIC, "SERBIAN_CYRILLIC" },
#endif
#ifdef SUBLANG_SERBIAN_LATIN
  { SUBLANG_SERBIAN_LATIN, "SERBIAN_LATIN" },
#endif
  { LANG_SPANISH, 0 },
#ifdef SUBLANG_SPANISH
  { SUBLANG_SPANISH, "SPANISH" },
#endif
#ifdef SUBLANG_SPANISH_ARGENTINA
  { SUBLANG_SPANISH_ARGENTINA, "SPANISH_ARGENTINA" },
#endif
#ifdef SUBLANG_SPANISH_BOLIVIA
  { SUBLANG_SPANISH_BOLIVIA, "SPANISH_BOLIVIA" },
#endif
#ifdef SUBLANG_SPANISH_CHILE
  { SUBLANG_SPANISH_CHILE, "SPANISH_CHILE" },
#endif
#ifdef SUBLANG_SPANISH_COLOMBIA
  { SUBLANG_SPANISH_COLOMBIA, "SPANISH_COLOMBIA" },
#endif
#ifdef SUBLANG_SPANISH_COSTA_RICA
  { SUBLANG_SPANISH_COSTA_RICA, "SPANISH_COSTA_RICA" },
#endif
#ifdef SUBLANG_SPANISH_DOMINICAN_REPUBLIC
  { SUBLANG_SPANISH_DOMINICAN_REPUBLIC, "SPANISH_DOMINICAN_REPUBLIC" },
#endif
#ifdef SUBLANG_SPANISH_ECUADOR
  { SUBLANG_SPANISH_ECUADOR, "SPANISH_ECUADOR" },
#endif
#ifdef SUBLANG_SPANISH_EL_SALVADOR
  { SUBLANG_SPANISH_EL_SALVADOR, "SPANISH_EL_SALVADOR" },
#endif
#ifdef SUBLANG_SPANISH_GUATEMALA
  { SUBLANG_SPANISH_GUATEMALA, "SPANISH_GUATEMALA" },
#endif
#ifdef SUBLANG_SPANISH_HONDURAS
  { SUBLANG_SPANISH_HONDURAS, "SPANISH_HONDURAS" },
#endif
#ifdef SUBLANG_SPANISH_MEXICAN
  { SUBLANG_SPANISH_MEXICAN, "SPANISH_MEXICAN" },
#endif
#ifdef SUBLANG_SPANISH_MODERN
  { SUBLANG_SPANISH_MODERN, "SPANISH_MODERN" },
#endif
#ifdef SUBLANG_SPANISH_NICARAGUA
  { SUBLANG_SPANISH_NICARAGUA, "SPANISH_NICARAGUA" },
#endif
#ifdef SUBLANG_SPANISH_PANAMA
  { SUBLANG_SPANISH_PANAMA, "SPANISH_PANAMA" },
#endif
#ifdef SUBLANG_SPANISH_PARAGUAY
  { SUBLANG_SPANISH_PARAGUAY, "SPANISH_PARAGUAY" },
#endif
#ifdef SUBLANG_SPANISH_PERU
  { SUBLANG_SPANISH_PERU, "SPANISH_PERU" },
#endif
#ifdef SUBLANG_SPANISH_PUERTO_RICO
  { SUBLANG_SPANISH_PUERTO_RICO, "SPANISH_PUERTO_RICO" },
#endif
#ifdef SUBLANG_SPANISH_URUGUAY
  { SUBLANG_SPANISH_URUGUAY, "SPANISH_URUGUAY" },
#endif
#ifdef SUBLANG_SPANISH_VENEZUELA
  { SUBLANG_SPANISH_VENEZUELA, "SPANISH_VENEZUELA" },
#endif
  { LANG_SWEDISH, 0 },
#ifdef SUBLANG_SWEDISH
  { SUBLANG_SWEDISH, "SWEDISH" },
#endif
#ifdef SUBLANG_SWEDISH_FINLAND
  { SUBLANG_SWEDISH_FINLAND, "SWEDISH_FINLAND" },
#endif
  { LANG_URDU, 0 },
#ifdef SUBLANG_URDU_INDIA
  { SUBLANG_URDU_INDIA, "URDU_INDIA" },
#endif
#ifdef SUBLANG_URDU_PAKISTAN
  { SUBLANG_URDU_PAKISTAN, "URDU_PAKISTAN" },
#endif
  { LANG_UZBEK, 0 },
#ifdef SUBLANG_UZBEK_CYRILLIC
  { SUBLANG_UZBEK_CYRILLIC, "UZBEK_CYRILLIC" },
#endif
#ifdef SUBLANG_UZBEK_LATIN
  { SUBLANG_UZBEK_LATIN, "UZBEK_LATIN" },
#endif
};

static int
lang_to_langcode (Lisp_Object lang, struct lang_to_string *table,
		  int table_size)
{
  int i;

  for (i = 0; i < table_size; i++)
    if (!qxestrcmp_ascii (XSTRING_DATA (lang), table[i].string))
      return table[i].code;
  return -1;
}

static int
sublang_to_langcode (Lisp_Object lang, struct lang_to_string *table,
		     int table_size)
{
  int i;

  for (i = 0; i < table_size; i++)
    if (table[i].string &&
	!qxestrcmp_ascii (XSTRING_DATA (lang), table[i].string))
      return table[i].code;

  if (!qxestrcmp_ascii (XSTRING_DATA (lang), "NEUTRAL"))
    return SUBLANG_NEUTRAL;
  if (!qxestrcmp_ascii (XSTRING_DATA (lang), "DEFAULT"))
    return SUBLANG_DEFAULT;
  if (!qxestrcmp_ascii (XSTRING_DATA (lang), "SYS_DEFAULT"))
    return SUBLANG_SYS_DEFAULT;

  return -1;
}

static Lisp_Object
langcode_to_lang (int code, struct lang_to_string *table,
		  int table_size)
{
  int i;

  for (i = 0; i < table_size; i++)
    if (code == table[i].code)
      return build_ascstring (table[i].string);
  return Qnil;
}

static Lisp_Object
sublangcode_to_lang (int lang, int sublang, struct lang_to_string *table,
		     int table_size)
{
  int i;
  int found_lang = 0;

  for (i = 0; i < table_size; i++)
    {
      if (found_lang)
	{
	  if (!table[i].string)
	    break;
	  if (sublang == table[i].code)
	    return build_ascstring (table[i].string);
	}
      else if (!table[i].string && lang == table[i].code)
	found_lang = 1;
    }

  switch (sublang)
    {
    case SUBLANG_NEUTRAL:
      return build_ascstring ("NEUTRAL");
    case SUBLANG_DEFAULT:
      return build_ascstring ("DEFAULT");
    case SUBLANG_SYS_DEFAULT:
      return build_ascstring ("SYS_DEFAULT");
    }

  return Qnil;
}

static LCID
locale_to_lcid (Lisp_Object locale)
{
  int langcode, sublangcode;
  Lisp_Object lang, sublang;

  if (STRINGP (locale))
    {
      lang = locale;
      sublang = Qnil;
    }
  else if (CONSP (locale))
    {
      CHECK_STRING (XCAR (locale));
      CHECK_STRING (XCDR (locale));
      lang = XCAR (locale);
      sublang = XCDR (locale);
    }
  else
    invalid_argument ("Locale must be LANG or (LANG . SUBLANG)", locale);

  langcode = lang_to_langcode (lang, lang_to_string_table,
			       countof (lang_to_string_table));

  if (langcode < 0)
    invalid_constant ("Unrecognized language", lang);

  if (!NILP (sublang))
    {
      sublangcode = sublang_to_langcode (sublang, sublang_to_string_table,
					 countof (sublang_to_string_table));
      if (sublangcode < 0)
	invalid_constant ("Unrecognized sublanguage", sublang);
    }
  else
    sublangcode = SUBLANG_DEFAULT;

  return MAKELCID (MAKELANGID (langcode, sublangcode),
		   SORT_DEFAULT);
}

static Lisp_Object
lcid_to_locale (LCID lcid)
{
  int langid = LANGIDFROMLCID (lcid);
  int langcode = PRIMARYLANGID (langid);
  int sublangcode = SUBLANGID (langid);

  return Fcons (langcode_to_lang (langcode, lang_to_string_table,
				  countof (lang_to_string_table)),
		sublangcode_to_lang (langcode, sublangcode,
				     sublang_to_string_table,
				     countof (sublang_to_string_table)));
}

int
mswindows_locale_to_code_page (LCID lcid)
{
  char codepagestr[10];

  GetLocaleInfoA (lcid, LOCALE_IDEFAULTANSICODEPAGE, codepagestr, 10);
  return atoi (codepagestr);
}

int
mswindows_locale_to_oem_code_page (LCID lcid)
{
  char codepagestr[10];

  GetLocaleInfoA (lcid, LOCALE_IDEFAULTCODEPAGE, codepagestr, 10);
  return atoi (codepagestr);
}

static void
set_current_lcid (LCID lcid)
{
  int cp;

  /* This will fail under Win9x, so we remember our own locale rather than
     consulting GetThreadLocale. */
  SetThreadLocale (lcid);
  current_locale = lcid;
  cp = mswindows_locale_to_code_page (lcid);
#ifndef NO_EXT_MULTIBYTE_FEATURES
  _setmbcp (cp);
#endif
}

DEFUN ("mswindows-set-current-locale", Fmswindows_set_current_locale,
       1, 1, 0, /*
Set the current MS Windows locale.

LOCALE should a language string, or a cons (LANG . SUBLANG).
If SUBLANG is omitted, "SUBLANG_DEFAULT" is used.

Recognized language names are
\(some may not be recognized if the compiler is older than VC++ 6.0)

"AFRIKAANS"
"ALBANIAN"
"ARABIC"
"ARMENIAN"
"ASSAMESE"
"AZERI"
"BASQUE"
"BELARUSIAN"
"BENGALI"
"BULGARIAN"
"CATALAN"
"CHINESE"
"CROATIAN"
"CZECH"
"DANISH"
"DUTCH"
"ENGLISH"
"ESTONIAN"
"FAEROESE"
"FARSI"
"FINNISH"
"FRENCH"
"GEORGIAN"
"GERMAN"
"GREEK"
"GUJARATI"
"HEBREW"
"HINDI"
"HUNGARIAN"
"ICELANDIC"
"INDONESIAN"
"ITALIAN"
"JAPANESE"
"KANNADA"
"KASHMIRI"
"KAZAK"
"KONKANI"
"KOREAN"
"LATVIAN"
"LITHUANIAN"
"MACEDONIAN"
"MALAY"
"MALAYALAM"
"MANIPURI"
"MARATHI"
"NEPALI"
"NEUTRAL"
"NORWEGIAN"
"ORIYA"
"POLISH"
"PORTUGUESE"
"PUNJABI"
"ROMANIAN"
"RUSSIAN"
"SANSKRIT"
"SERBIAN"
"SINDHI"
"SLOVAK"
"SLOVENIAN"
"SPANISH"
"SWAHILI"
"SWEDISH"
"TAMIL"
"TATAR"
"TELUGU"
"THAI"
"TURKISH"
"UKRAINIAN"
"URDU"
"UZBEK"
"VIETNAMESE"

Recognized sub-language names are
\(some may not be recognized if the compiler is older than VC++ 6.0)

"ARABIC_ALGERIA"
"ARABIC_BAHRAIN"
"ARABIC_EGYPT"
"ARABIC_IRAQ"
"ARABIC_JORDAN"
"ARABIC_KUWAIT"
"ARABIC_LEBANON"
"ARABIC_LIBYA"
"ARABIC_MOROCCO"
"ARABIC_OMAN"
"ARABIC_QATAR"
"ARABIC_SAUDI_ARABIA"
"ARABIC_SYRIA"
"ARABIC_TUNISIA"
"ARABIC_UAE"
"ARABIC_YEMEN"
"AZERI_CYRILLIC"
"AZERI_LATIN"
"CHINESE_HONGKONG"
"CHINESE_MACAU"
"CHINESE_SIMPLIFIED"
"CHINESE_SINGAPORE"
"CHINESE_TRADITIONAL"
"DEFAULT"
"DUTCH"
"DUTCH_BELGIAN"
"ENGLISH_AUS"
"ENGLISH_BELIZE"
"ENGLISH_CAN"
"ENGLISH_CARIBBEAN"
"ENGLISH_EIRE"
"ENGLISH_JAMAICA"
"ENGLISH_NZ"
"ENGLISH_PHILIPPINES"
"ENGLISH_SOUTH_AFRICA"
"ENGLISH_TRINIDAD"
"ENGLISH_UK"
"ENGLISH_US"
"ENGLISH_ZIMBABWE"
"FRENCH"
"FRENCH_BELGIAN"
"FRENCH_CANADIAN"
"FRENCH_LUXEMBOURG"
"FRENCH_MONACO"
"FRENCH_SWISS"
"GERMAN"
"GERMAN_AUSTRIAN"
"GERMAN_LIECHTENSTEIN"
"GERMAN_LUXEMBOURG"
"GERMAN_SWISS"
"ITALIAN"
"ITALIAN_SWISS"
"KASHMIRI_INDIA"
"KOREAN"
"KOREAN_JOHAB" (NOTE: omitted in Visual C++ 6.0 and later)
"LITHUANIAN"
"LITHUANIAN_CLASSIC"
"MALAY_BRUNEI_DARUSSALAM"
"MALAY_MALAYSIA"
"NEPALI_INDIA"
"NEUTRAL"
"NORWEGIAN_BOKMAL"
"NORWEGIAN_NYNORSK"
"PORTUGUESE"
"PORTUGUESE_BRAZILIAN"
"SERBIAN_CYRILLIC"
"SERBIAN_LATIN"
"SPANISH"
"SPANISH_ARGENTINA"
"SPANISH_BOLIVIA"
"SPANISH_CHILE"
"SPANISH_COLOMBIA"
"SPANISH_COSTA_RICA"
"SPANISH_DOMINICAN_REPUBLIC"
"SPANISH_ECUADOR"
"SPANISH_EL_SALVADOR"
"SPANISH_GUATEMALA"
"SPANISH_HONDURAS"
"SPANISH_MEXICAN"
"SPANISH_MODERN"
"SPANISH_NICARAGUA"
"SPANISH_PANAMA"
"SPANISH_PARAGUAY"
"SPANISH_PERU"
"SPANISH_PUERTO_RICO"
"SPANISH_URUGUAY"
"SPANISH_VENEZUELA"
"SWEDISH"
"SWEDISH_FINLAND"
"SYS_DEFAULT"
"URDU_INDIA"
"URDU_PAKISTAN"
"UZBEK_CYRILLIC"
"UZBEK_LATIN"
*/
       (locale))
{
  LCID lcid = locale_to_lcid (locale);

  set_current_lcid (lcid);
  return Qnil;
}

#ifdef DEBUG_XEMACS

int getacp (void);
int
getacp (void)
{
  return GetACP ();
}

#endif /* DEBUG_XEMACS */

LCID
mswindows_current_locale (void)
{
  /* Even if SetThreadLocale() failed, return the right locale anyway */
  return current_locale;
}

DEFUN ("mswindows-current-locale", Fmswindows_current_locale,
       0, 0, 0, /*
Return the current MS Windows locale.

The return value will be a cons (LANG . SUBLANG).  See
`mswindows-set-current-locale' for more info.
*/
       ())
{
  return lcid_to_locale (mswindows_current_locale ());
}

DEFUN ("mswindows-user-default-locale", Fmswindows_user_default_locale,
       0, 0, 0, /*
Return the MS Windows user-default locale.
*/
       ())
{
  return lcid_to_locale (GetUserDefaultLCID ());
}

DEFUN ("mswindows-system-default-locale", Fmswindows_system_default_locale,
       0, 0, 0, /*
Return the MS Windows system-default locale.
*/
       ())
{
  return lcid_to_locale (GetSystemDefaultLCID ());
}

DEFUN ("mswindows-locale-code-page", Fmswindows_locale_code_page,
       0, 1, 0, /*
Return the (ANSI) code page of the specified MS Windows locale.
If LOCALE is nil or omitted, the current locale is used.
*/
       (locale))
{
  LCID lcid = NILP (locale) ? current_locale : locale_to_lcid (locale);
  return make_fixnum (mswindows_locale_to_code_page (lcid));
}

DEFUN ("mswindows-locale-oem-code-page", Fmswindows_locale_oem_code_page,
       0, 1, 0, /*
Return the OEM code page of the specified MS Windows locale.
If LOCALE is nil or omitted, the current locale is used.
*/
       (locale))
{
  LCID lcid = NILP (locale) ? current_locale : locale_to_lcid (locale);
  return make_fixnum (mswindows_locale_to_oem_code_page (lcid));
}

static DWORD
int_from_hex (Ascbyte *s)
{
  DWORD val = 0;
  static Ascbyte hex[] = "0123456789abcdefABCDEF";
  Ascbyte *p;

  while (*s && (p = strchr (hex, *s)) != NULL)
    {
      int digit = p - hex;
      if (digit > 15)
	digit -= 6;
      val = val * 16 + digit;
      s++;
    }
  return val;
}

/* We need to build a global list, since the EnumSystemLocale callback
   function isn't given a context pointer.  */
static Lisp_Object Vmswindows_valid_locales;

static BOOL CALLBACK
enum_locale_fn (Ascbyte *localeNum)
{
  DWORD id = int_from_hex (localeNum);
  Vmswindows_valid_locales =
    Fcons (lcid_to_locale ((LCID) id), Vmswindows_valid_locales);
  return TRUE;
}

DEFUN ("mswindows-supported-locales", Fmswindows_supported_locales,
       0, 0, 0, /*
Return a list of the supported MS Windows locales on this system.
*/
       ())
{
  Vmswindows_valid_locales = Qnil;

  /* Use the ANSI version because the return value is just a hex number. */
  EnumSystemLocalesA (enum_locale_fn, LCID_SUPPORTED);

  Vmswindows_valid_locales = Fnreverse (Vmswindows_valid_locales);
  return Vmswindows_valid_locales;
}

/************************************************************************/
/*                            Mule functions                            */
/************************************************************************/

DEFUN ("mswindows-charset-code-page", 
       Fmswindows_charset_code_page, 1, 1, 0, /*
Return the code page for the CHARSET.

#### This function may be changed in the near future.

Currently defined Windows code pages include (along with their status
as Ansi, OEM, Mac, EBCDIC, or some combination):

EBCDIC      037 EBCDIC 
OEM         437 MS-DOS United States 
EBCDIC      500 EBCDIC "500V1" 
OEM         708 Arabic (ASMO 708) 
OEM         709 Arabic (ASMO 449+, BCON V4) 
OEM         710 Arabic (Transparent Arabic) 
OEM         720 Arabic (Transparent ASMO) 
OEM         737 Greek (formerly 437G) 
OEM         775 Baltic 
OEM         850 MS-DOS Multilingual (Latin I) 
OEM         852 MS-DOS Slavic (Latin II) 
OEM         855 IBM Cyrillic (primarily Russian) 
OEM         857 IBM Turkish 
OEM         860 MS-DOS Portuguese 
OEM         861 MS-DOS Icelandic 
OEM         862 Hebrew 
OEM         863 MS-DOS Canadian-French 
OEM         864 Arabic 
OEM         865 MS-DOS Nordic 
OEM         866 MS-DOS Russian 
OEM         869 IBM Modern Greek 
Ansi/OEM    874 Thai 
EBCDIC      875 EBCDIC 
Ansi/OEM    932 Japanese 
Ansi/OEM    936 Chinese (PRC, Singapore) 
Ansi/OEM    949 Korean 
Ansi/OEM    950 Chinese (Taiwan; Hong Kong SAR, PRC)  
EBCDIC      1026 EBCDIC 
ANSI        1200 Unicode (BMP of ISO 10646) 
ANSI        1250 Windows 3.1 Eastern European  
ANSI        1251 Windows 3.1 Cyrillic 
ANSI        1252 Windows 3.1 US (ANSI) 
ANSI        1253 Windows 3.1 Greek 
ANSI        1254 Windows 3.1 Turkish 
ANSI        1255 Hebrew 
ANSI        1256 Arabic 
ANSI        1257 Baltic 
ANSI        1258 VietNam
Ansi/OEM    1361 Korean (Johab) 
Mac         10000 Macintosh Roman 
Mac         10001 Macintosh Japanese 
Mac         10006 Macintosh Greek I 
Mac         10007 Macintosh Cyrillic 
Mac         10029 Macintosh Latin 2 
Mac         10079 Macintosh Icelandic 
Mac         10081 Macintosh Turkish

A code page is a set of characters, along with an enumeration of these
characters and an encoding of them in a byte stream.  Thus, in XEmacs
parlance it defines both a "charset" and a "coding system" for this
charset.  Traditional encodings are either simple one-byte encodings, or
combination one-byte/two-byte encodings (aka MBCS encodings, where MBCS
stands for "Multibyte Character Set") with the following properties:

-- all characters are encoded as a one-byte or two-byte sequence
-- the encoding is stateless (non-modal)
-- the lower 128 bytes are compatible with ASCII
-- in the higher bytes, the value of the first byte ("lead byte")
   determines whether a second byte follows
-- the values used for second bytes may overlap those used for first bytes,
   and (in some encodings) include values in the low half; thus, moving
   backwards is hard, and pure-ASCII algorithms (e.g. finding the next slash)
   will fail unless rewritten to be MBCS-aware (neither of these problems
   exist in UTF-8 or in the XEmacs internal string encoding)

Recent code pages, however, do not necessarily follow these properties --
code pages have been expanded to include arbitrary encodings, such as UTF-8
\(may have more than two bytes per character) and ISO-2022-JP (complex modal
encoding).

Every locale has four associated code pages: ANSI (an international
standard or some Microsoft-created approximation; the native code page
under Windows), OEM (a DOS encoding, still used in the FAT file system),
Mac (an encoding used on the Macintosh) and EBCDIC (a non-ASCII-compatible
encoding used on IBM mainframes, originally based on the BCD or
"binary-coded decimal" encoding of numbers).  All code pages associated
with a locale follow (as far as I know) the properties listed above for
traditional code pages.
*/
       (charset))
{
  charset = Fget_charset (charset);
  return Fgethash (charset, Vmswindows_charset_code_page_table, Qnil);
}

DEFUN ("mswindows-set-charset-code-page", 
       Fmswindows_set_charset_code_page, 2, 2, 0, /*
Set the CODE-PAGE for the CHARSET.

#### This function may be changed once full Unicode support is present.
*/
       (charset, code_page))
{
  charset = Fget_charset (charset);
  CHECK_FIXNUM (code_page);
  Fputhash (charset, code_page, Vmswindows_charset_code_page_table);
  return Qnil;
}

Lisp_Object 
mswindows_get_code_page_charset (int code_page)
{
  Lisp_Object charset_tail;
  Lisp_Object charset = Qunbound;

  LIST_LOOP (charset_tail, Fcharset_list ())
    {
      Lisp_Object charset_code_page;

      charset_code_page = Fmswindows_charset_code_page (XCAR (charset_tail));
      if (FIXNUMP (charset_code_page) &&
	  code_page == XFIXNUM (charset_code_page))
	{
	  charset = Fget_charset (XCAR (charset_tail));
	  break;
	}
    }
  return charset;
}

DEFUN ("mswindows-charset-registry", 
       Fmswindows_charset_registry, 1, 1, 0, /*
Return the registry for the CHARSET.
This is the last item in an MS Windows font spec.

#### This function may be changed in the near future.
*/
       (charset))
{
  charset = Fget_charset (charset);
  return Fgethash (charset, Vmswindows_charset_registry_table, Qnil);
}

DEFUN ("mswindows-set-charset-registry", 
       Fmswindows_set_charset_registry, 2, 2, 0, /*
Set the REGISTRY for the CHARSET.

#### This function may be changed once full Unicode support is present.
*/
       (charset, registry))
{
  charset = Fget_charset (charset);
  CHECK_STRING (registry);
  Fputhash (charset, registry, Vmswindows_charset_registry_table);
  invalidate_charset_font_caches (charset);
  face_property_was_changed (Vdefault_face, Qfont, Qglobal);
  return Qnil;
}

#if 0 /* Unused */

static Lisp_Object 
mswindows_get_registry_charset (Ibyte *registry)
{
  Lisp_Object charset_tail;
  Lisp_Object charset = Qunbound;

  LIST_LOOP (charset_tail, Fcharset_list ())
    {
      Lisp_Object charset_registry;

      charset_registry = Fmswindows_charset_registry (XCAR (charset_tail));
      if (STRINGP (charset_registry) &&
	  !qxestrcasecmp (XSTRING_DATA (charset_registry), registry))
	{
	  charset = Fget_charset (XCAR (charset_tail));
	  break;
	}
    }
  return charset;
}

#endif /* 0 */



#if 0 /* #### from Emacs 20.6; consider porting */

DEFUN ("mswindows-get-locale-info", Fmswindows_get_locale_info, 1, 2, 0, /*
Return information about the Windows locale LCID.
By default, return a three letter locale code which encodes the default
language as the first two characters, and the country or regionial variant
as the third letter.  For example, ENU refers to `English (United States)',
while ENC means `English (Canadian)'.

If the optional argument LONGFORM is t, the long form of the locale
name is returned, e.g. `English (United States)' instead; if LONGFORM
is a number, it is interpreted as an LCTYPE constant and the corresponding
locale information is returned.

If LCID (a 16-bit number) is not a valid locale, the result is nil.
*/
       (lcid, longform))
{
  int got_abbrev;
  int got_full;
  Extbyte abbrev_name[32] = { 0 };
  Extbyte full_name[256] = { 0 };

  CHECK_FIXNUM (lcid);

  if (!IsValidLocale (XFIXNUM (lcid), LCID_SUPPORTED))
    return Qnil;

  if (NILP (longform))
    {
      got_abbrev = qxeGetLocaleInfo (XFIXNUM (lcid),
				     LOCALE_SABBREVLANGNAME |
				     LOCALE_USE_CP_ACP,
				     abbrev_name, sizeof (abbrev_name));
      if (got_abbrev)
	return build_tstr_string (abbrev_name);
    }
  else if (EQ (longform, Qt))
    {
      got_full = qxeGetLocaleInfo (XFIXNUM (lcid),
				   LOCALE_SLANGUAGE | LOCALE_USE_CP_ACP,
				   full_name, sizeof (full_name));
      if (got_full)
	return build_tstr_string (full_name);
    }
  else if (NUMBERP (longform))
    {
      got_full = qxeGetLocaleInfo (XFIXNUM (lcid),
				   XFIXNUM (longform),
				   full_name, sizeof (full_name));
      if (got_full)
	return build_tstr_string (full_name);
    }

  return Qnil;
}

/* We need to build a global list, since the EnumCodePages callback
   function isn't given a context pointer.  */
Lisp_Object Vmswindows_valid_code_pages;

BOOL CALLBACK enum_code_page_fn (LPTSTR codepageNum)
{
  DWORD id = atoi (codepageNum);
  Vmswindows_valid_code_pages = Fcons (make_fixnum (id), Vmswindows_valid_code_pages);
  return TRUE;
}

DEFUN ("mswindows-get-valid-code-pages", Fmswindows_get_valid_code_pages, 0, 0, 0, /*
Return list of all valid Windows code pages.
*/
       ())
{
  Vmswindows_valid_code_pages = Qnil;

  EnumSystemCodePages (enum_code_page_fn, CP_SUPPORTED);

  Vmswindows_valid_code_pages = Fnreverse (Vmswindows_valid_code_pages);
  return Vmswindows_valid_code_pages;
}

DEFUN ("mswindows-get-console-code-page", Fmswindows_get_console_code_page, 0, 0, 0, /*
Return current Windows code page for console input.
*/
       ())
{
  return make_fixnum (GetConsoleCP ());
}

DEFUN ("mswindows-set-console-code-page", Fmswindows_set_console_code_page, 1, 1, 0, /*
Make Windows code page CP be the current code page setting for Emacs.
The code page setting affects keyboard input and display in tty mode.
If successful, the new CP is returned, otherwise nil.
*/
       (cp))
{
  CHECK_FIXNUM (cp);

  if (!IsValidCodePage (XFIXNUM (cp)))
    return Qnil;

  if (!SetConsoleCP (XFIXNUM (cp)))
    return Qnil;

  return make_fixnum (GetConsoleCP ());
}

DEFUN ("mswindows-get-console-output-code-page", Fmswindows_get_console_output_code_page, 0, 0, 0, /*
Return current Windows code page for console output.
*/
       ())
{
  return make_fixnum (GetConsoleOutputCP ());
}

DEFUN ("mswindows-set-console-output-code-page", Fmswindows_set_console_output_code_page, 1, 1, 0, /*
Make Windows code page CP be the current code page setting for Emacs.
The code page setting affects keyboard input and display in tty mode.
If successful, the new CP is returned, otherwise nil.
*/
       (cp))
{
  CHECK_FIXNUM (cp);

  if (!IsValidCodePage (XFIXNUM (cp)))
    return Qnil;

  if (!SetConsoleOutputCP (XFIXNUM (cp)))
    return Qnil;

  return make_fixnum (GetConsoleOutputCP ());
}

DEFUN ("mswindows-get-code-page-charset", Fmswindows_get_code_page_charset, 1, 1, 0, /*
Return charset of code page CP.
Returns nil if the code page is not valid.
*/
       (cp))
{
  CHARSETINFO info;

  CHECK_FIXNUM (cp);

  if (!IsValidCodePage (XFIXNUM (cp)))
    return Qnil;

  if (TranslateCharsetInfo ((DWORD *) XFIXNUM (cp), &info, TCI_SRCCODEPAGE))
    return make_fixnum (info.ciCharset);

  return Qnil;
}

DEFUN ("mswindows-get-valid-keyboard-layouts", Fmswindows_get_valid_keyboard_layouts, 0, 0, 0, /*
Return list of Windows keyboard languages and layouts.
The return value is a list of pairs of language id and layout id.
*/
       ())
{
  int num_layouts = GetKeyboardLayoutList (0, NULL);
  HKL *layouts = alloca_array (HKL, num_layouts);
  Lisp_Object obj = Qnil;

  if (GetKeyboardLayoutList (num_layouts, layouts) == num_layouts)
    {
      while (--num_layouts >= 0)
	{
	  DWORD kl = (DWORD) layouts[num_layouts];

	  obj = Fcons (Fcons (make_fixnum (kl & 0xffff),
			      make_fixnum ((kl >> 16) & 0xffff)),
		       obj);
	}
    }

  return obj;
}

DEFUN ("mswindows-get-keyboard-layout", Fmswindows_get_keyboard_layout, 0, 0, 0, /*
Return current Windows keyboard language and layout.
The return value is the cons of the language id and the layout id.
*/
       ())
{
  DWORD kl = (DWORD) GetKeyboardLayout (dwWindowsThreadId);

  return Fcons (make_fixnum (kl & 0xffff),
		make_fixnum ((kl >> 16) & 0xffff));
}

DEFUN ("mswindows-set-keyboard-layout", Fmswindows_set_keyboard_layout, 1, 1, 0, /*
Make LAYOUT be the current keyboard layout for Emacs.
The keyboard layout setting affects interpretation of keyboard input.
If successful, the new layout id is returned, otherwise nil.
*/
       (layout))
{
  DWORD kl;

  CHECK_CONS (layout);
  CHECK_FIXNUM (XCAR (layout)));
  CHECK_FIXNUM (XCDR (layout)));

  kl = (XFIXNUM (XCAR (layout))) & 0xffff)
    | (XFIXNUM (XCDR (layout))) << 16);

  if (!ActivateKeyboardLayout ((HKL) kl, 0))
    return Qnil;

  return Fmswindows_get_keyboard_layout ();
}

#endif /* 0 */


/* input method functions. */

#ifdef HAVE_MS_WINDOWS

void
mswindows_start_ime_composition (struct frame *f)
{
  COMPOSITIONFORM form;
  HWND hwnd = FRAME_MSWINDOWS_HANDLE (f);
  HIMC himc = ImmGetContext (hwnd);

  /* Set a position of composition window. */
  xzero (form);
  form.dwStyle = CFS_POINT;
  form.ptCurrentPos.x = FRAME_MSWINDOWS_CURSOR_X (f);
  form.ptCurrentPos.y = FRAME_MSWINDOWS_CURSOR_Y (f);
  ImmSetCompositionWindow (himc, &form);

  /* Set composition window font same as current face one. */
  {
    LOGFONTW old_logfont;
    CHARSETINFO info;
    Lisp_Object charset;

    /* Get Mule charset from current ime font charset. */
    qxeImmGetCompositionFont (himc, &old_logfont);
    TranslateCharsetInfo ((DWORD *) (DWORD) old_logfont.lfCharSet, &info,
			  TCI_SRCCHARSET);
    charset = mswindows_get_code_page_charset (info.ciACP);

    if (CHARSETP (charset))
      {
	Lisp_Object window = FRAME_SELECTED_WINDOW (f);
	struct window *w = XWINDOW (window);
	face_index findex = FRAME_MSWINDOWS_CURSOR_FINDEX (f);
	struct face_cachel *cachel = WINDOW_FACE_CACHEL (w, findex);
	Lisp_Object face_font = FACE_CACHEL_FONT (cachel, charset);

	if (!FONT_INSTANCEP (face_font))
	  face_font = 
	    ensure_face_cachel_contains_charset (cachel, window, charset);

	if (!EQ (face_font, Vthe_null_font_instance))
	  {
	    LOGFONTW new_logfont;

	    /* Get LOGFONT from the face font */
	    if (qxeGetObject (FONT_INSTANCE_MSWINDOWS_HFONT_VARIANT
			      (XFONT_INSTANCE (face_font), 
			       cachel->underline, cachel->strikethru),
			      sizeof (LOGFONTW), (void*) &new_logfont))
	      qxeImmSetCompositionFont (himc, &new_logfont);
	  }
      }
  }
  ImmReleaseContext (hwnd, himc);
  return;
}

#endif /* HAVE_MS_WINDOWS */

#else /* not MULE */

int
mswindows_locale_to_code_page (LCID UNUSED (lcid))
{
  return CP_ACP;
}

LCID
mswindows_current_locale (void)
{
  /* In non-MULE version just return the default locale */
  return GetUserDefaultLCID ();
}

#endif /* MULE */


#ifdef CYGWIN

/* based on newlib str*() */

#ifndef HAVE_WCSCMP

int
wcscmp (const wchar_t *s1, const wchar_t *s2)
{
  if (s1 == NULL || s2 == NULL) return NULL;
  while (*s1 != '\0' && *s1 == *s2)
    {
      s1++;
      s2++;
    }

  return *s1 - *s2;
}

#endif /* not HAVE_WCSCMP */

#ifndef HAVE_WCSLEN

size_t
wcslen (const wchar_t *str)
{
  if (str == NULL) return NULL;
  const wchar_t *start = str;

  while (*str)
    str++;

  return str - start;
}

#endif /* not HAVE_WCSLEN */

wchar_t *
wcsncpy (wchar_t *dst0, const wchar_t *src0, size_t count)
{
  wchar_t *dscan;
  const wchar_t *sscan;

  if (dst0 == NULL || src0 == NULL) return NULL;
  dscan = dst0;
  sscan = src0;
  while (count > 0)
    {
      --count;
      if ((*dscan++ = *sscan++) == '\0')
	break;
    }
  while (count-- > 0)
    *dscan++ = '\0';

  return dst0;
}

wchar_t *
wcscpy (wchar_t *dst0, const wchar_t *src0)
{
  if (dst0 == NULL || src0 == NULL) return NULL;
  wchar_t *s = dst0;

  while ((*dst0++ = *src0++))
    ;

  return s;
}

wchar_t *
wcsdup (const wchar_t *str)
{
  if (str == NULL) return NULL;
  int len = wcslen (str) + 1;
  wchar_t *val = xnew_array (wchar_t, len);

  if (val == 0) return 0;
  return (wchar_t *) memcpy (val, str, len * sizeof (wchar_t));
}

#endif /* CYGWIN */


/************************************************************************/
/*                 MS Windows multibyte-to-unicode methods              */
/************************************************************************/

enum mswindows_multibyte_cp_type
{
  MULTIBYTE_ANSI,
  MULTIBYTE_OEM,
  MULTIBYTE_EBCDIC,
  MULTIBYTE_MAC
};

enum mswindows_multibyte_locale_type
{
  MULTIBYTE_SPECIFIED_LOCALE,
  MULTIBYTE_SPECIFIED_CODE_PAGE,
  MULTIBYTE_CURRENT,
  MULTIBYTE_USER_DEFAULT,
  MULTIBYTE_SYSTEM_DEFAULT
};

struct mswindows_multibyte_to_unicode_coding_system
{
  enum mswindows_multibyte_cp_type cp_type;
  enum mswindows_multibyte_locale_type locale_type;
  LCID locale; /* if locale_type is MULTIBYTE_SPECIFIED_LOCALE */
  int cp; /* if locale_type is MULTIBYTE_SPECIFIED_CODE_PAGE */
};

struct mswindows_multibyte_to_unicode_coding_stream
{
  int partial_byte;
  int partial_byte_present;
  int cp;
};

static const struct memory_description
  mswindows_multibyte_to_unicode_coding_system_description[] = {
  { XD_END }
};

DEFINE_CODING_SYSTEM_TYPE_WITH_DATA (mswindows_multibyte_to_unicode);

static void
mswindows_multibyte_to_unicode_init (Lisp_Object codesys)
{
  struct mswindows_multibyte_to_unicode_coding_system *data =
    XCODING_SYSTEM_TYPE_DATA (codesys, mswindows_multibyte_to_unicode);

  data->cp_type = MULTIBYTE_ANSI;
  data->locale_type = MULTIBYTE_CURRENT;
}

static Lisp_Object
lcid_to_locale_mule_or_no (LCID USED_IF_MULE (lcid))
{
#ifdef MULE
  return lcid_to_locale (lcid);
#else
  return Fcons (build_ascstring ("NEUTRAL"), build_ascstring ("DEFAULT"));
#endif
}

static int
determine_code_page (Lisp_Object USED_IF_MULE (codesys))
{
#ifdef MULE
  LCID locale;
  struct mswindows_multibyte_to_unicode_coding_system *data =
    XCODING_SYSTEM_TYPE_DATA (codesys, mswindows_multibyte_to_unicode);

  switch (data->locale_type)
    {
    case MULTIBYTE_SPECIFIED_CODE_PAGE:
      return data->cp;
    case MULTIBYTE_SPECIFIED_LOCALE:
      locale = data->locale; break;
    case MULTIBYTE_CURRENT:
      locale = mswindows_current_locale (); break;
    case MULTIBYTE_USER_DEFAULT:
      locale = GetUserDefaultLCID (); break;
    case MULTIBYTE_SYSTEM_DEFAULT:
      locale = GetSystemDefaultLCID (); break;
    default:
      ABORT (); locale = 0;
    }

  switch (data->cp_type)
    {
    case MULTIBYTE_ANSI:
      return mswindows_locale_to_code_page (locale);
    case MULTIBYTE_OEM:
      return mswindows_locale_to_oem_code_page (locale);
    case MULTIBYTE_EBCDIC:
#ifdef LOCALE_IDEFAULTEBCDICCODEPAGE /* Doesn't exist under Cygwin */
      {
	char codepagestr[10];
	GetLocaleInfoA (locale, LOCALE_IDEFAULTEBCDICCODEPAGE, codepagestr,
			10);
	return atoi (codepagestr);
      }
#else
      invalid_operation ("Unable to determine EBCDIC code page for locale",
			 lcid_to_locale (locale));
      return 0;
#endif
    case MULTIBYTE_MAC:
#ifdef LOCALE_IDEFAULTMACCODEPAGE /* Doesn't exist under Cygwin */
      {
	char codepagestr[10];
	GetLocaleInfoA (locale, LOCALE_IDEFAULTMACCODEPAGE, codepagestr,
			10);
	return atoi (codepagestr);
      }
#else
      invalid_operation ("Unable to determine Mac code page for locale",
			 lcid_to_locale (locale));
      return 0;
#endif
    default:
      ABORT (); return 0;
    }
#else /* not MULE */
  return CP_ACP;
#endif
}

static int
mswindows_multibyte_to_unicode_putprop (Lisp_Object codesys,
					Lisp_Object key,
					Lisp_Object value)
{
  struct mswindows_multibyte_to_unicode_coding_system *data =
    XCODING_SYSTEM_TYPE_DATA (codesys, mswindows_multibyte_to_unicode);

  if (EQ (key, Qcode_page))
    {
      if (EQ (value, Qansi))
	data->cp_type = MULTIBYTE_ANSI;
      else if (EQ (value, Qoem))
	data->cp_type = MULTIBYTE_OEM;
      else if (EQ (value, Qebcdic))
	data->cp_type = MULTIBYTE_EBCDIC;
      else if (EQ (value, Qmac))
	data->cp_type = MULTIBYTE_MAC;
      else
	{
	  data->locale_type = MULTIBYTE_SPECIFIED_CODE_PAGE;
#ifdef HAVE_BIGNUM
          check_integer_range (value, Qzero, make_integer (INT_MAX));
	  data->cp = BIGNUMP (value) ? bignum_to_int (XBIGNUM_DATA (value)) : XFIXNUM (value);
#else
          CHECK_NATNUM (value);
          data->cp = XFIXNUM (value);
#endif
	}
    }
  else if (EQ (key, Qlocale))
    {
      if (EQ (value, Qcurrent))
	data->locale_type = MULTIBYTE_CURRENT;
      else if (EQ (value, Quser_default))
	data->locale_type = MULTIBYTE_USER_DEFAULT;
      else if (EQ (value, Qsystem_default))
	data->locale_type = MULTIBYTE_SYSTEM_DEFAULT;
      else
	{
	  data->locale_type = MULTIBYTE_SPECIFIED_LOCALE;
#ifdef MULE
	  data->locale = locale_to_lcid (value);
#else
	  data->locale = 0;
#endif
	}
    }
  else
    return 0;
  return 1;
}

static Lisp_Object
mswindows_multibyte_to_unicode_getprop (Lisp_Object coding_system,
					Lisp_Object prop)
{
  struct mswindows_multibyte_to_unicode_coding_system *data =
    XCODING_SYSTEM_TYPE_DATA (coding_system, mswindows_multibyte_to_unicode);

  if (EQ (prop, Qcode_page))
    {
      if (data->locale_type == MULTIBYTE_SPECIFIED_CODE_PAGE)
	return make_fixnum (data->cp);
      else
	switch (data->cp_type)
	  {
	  case MULTIBYTE_ANSI: return Qansi;
	  case MULTIBYTE_OEM: return Qoem;
	  case MULTIBYTE_EBCDIC: return Qebcdic;
	  case MULTIBYTE_MAC: return Qmac;
	  default: ABORT ();
	  }
    }
  else if (EQ (prop, Qlocale))
    {
      switch (data->locale_type)
	{
	case MULTIBYTE_CURRENT: return Qcurrent;
	case MULTIBYTE_USER_DEFAULT: return Quser_default;
	case MULTIBYTE_SYSTEM_DEFAULT: return Qsystem_default;
	case MULTIBYTE_SPECIFIED_LOCALE:
	  return lcid_to_locale_mule_or_no (data->locale);

	case MULTIBYTE_SPECIFIED_CODE_PAGE:
	  return Qnil;
	default: ABORT ();
	}
    }

  return Qunbound;
}

static void
mswindows_multibyte_to_unicode_print (Lisp_Object cs, Lisp_Object printcharfun,
				      int UNUSED (escapeflag))
{
  struct mswindows_multibyte_to_unicode_coding_system *data =
    XCODING_SYSTEM_TYPE_DATA (cs, mswindows_multibyte_to_unicode);

  write_ascstring (printcharfun, "(");
  if (data->locale_type == MULTIBYTE_SPECIFIED_CODE_PAGE)
    print_internal (make_fixnum (data->cp), printcharfun, 1);
  else
    {
      write_fmt_string_lisp (printcharfun, "%s, ", 1, mswindows_multibyte_to_unicode_getprop (cs, Qlocale));
      print_internal (mswindows_multibyte_to_unicode_getprop (cs, Qcode_page), printcharfun, 0);
    }
  write_ascstring (printcharfun, ")");
}

/* ----------------------------------------------------------------------- */
/*                      Unicode/Multibyte converters                       */
/* ----------------------------------------------------------------------- */

/* Convert multibyte to Unicode according to the specified code page
   and return the value as a malloc()ed string.  This currently exists
   because the TO_INTERNAL_FORMAT() mechanism -- the normal way to do
   such conversions -- has no way of passing in a parameter to control
   the operation.  We could use a global variable to pass this value
   in, but that runs the risk of causing problems due to reentrancy.
   (You might say, yeah, right, how can TO_INTERNAL_FORMAT() get
   called recursively merely when I'm doing a simple conversion
   operation?  It turns out this can and does happen, consistently, as
   a result of calling QUIT -- it happens consistently for complicated
   reasons outlined in event-msw.c, WM_KEYDOWN handling.)

   NOTE: These functions are also used at startup. */

Extbyte *
convert_multibyte_to_unicode_malloc (const Extbyte *src, Bytecount n,
				     int cp, Bytecount *size_out)
{
  Charcount nout = MultiByteToWideChar (cp, MBTOWC_OPTIONS, src, n, 0, 0);
  Extbyte *outp = xnew_array (Extbyte, nout * sizeof (WCHAR));

  MultiByteToWideChar (cp, MBTOWC_OPTIONS, src, n, (LPWSTR) outp, nout);
  if (size_out)
    *size_out = nout * sizeof (WCHAR);
  return outp;
}

Extbyte *
convert_unicode_to_multibyte_malloc (const Extbyte *src, Bytecount n,
				     int cp, Bytecount *size_out)
{
  Bytecount nout = WideCharToMultiByte (cp, WCTOMB_OPTIONS, (LPWSTR) src,
					n / sizeof (WCHAR),
					0, 0, WCTOMB_INVALID_STRING, 0);
  Extbyte *outp = xnew_array (Extbyte, nout);

  WideCharToMultiByte (cp, WCTOMB_OPTIONS, (LPWSTR) src,
		       n / sizeof (WCHAR), (LPSTR) outp, nout,
		       WCTOMB_INVALID_STRING, 0);
  if (size_out)
    *size_out = nout;
  return outp;
}

/* Convert MS Windows multibyte to internal, with specified code page.
   See above for why this exists, and the TO_INTERNAL_FORMAT() macros
   aren't just used. */

Ibyte *
convert_multibyte_to_internal_malloc (const Extbyte *src, Bytecount n,
				      int cp, Bytecount *size_out)
{
  Bytecount size;
  Extbyte *unidata = convert_multibyte_to_unicode_malloc (src, n, cp, &size);
  Ibyte *intdata;

  TO_INTERNAL_FORMAT (DATA, (unidata, size), MALLOC, (intdata, size),
		      Qmswindows_unicode);

  xfree (unidata);

  if (size_out)
    *size_out = size;

  return intdata;
}

static void
unicode_multibyte_convert_now_damn_it (const void *src, Bytecount src_size,
				       int to_unicode, void **dst,
				       Bytecount *dst_size, int cp)
{
  if (to_unicode)
    {
      Charcount nout = MultiByteToWideChar (cp, MBTOWC_OPTIONS, (LPSTR) src,
					    src_size, 0, 0);
      Extbyte *outp = (Extbyte *) stack_like_malloc (nout * sizeof (WCHAR));

      MultiByteToWideChar (cp, MBTOWC_OPTIONS, (LPSTR) src, src_size,
			   (LPWSTR) outp, nout);
      *dst = (void *) outp;
      *dst_size = nout * sizeof (WCHAR);
    }
  else
    {
      Bytecount nout = WideCharToMultiByte (cp, WCTOMB_OPTIONS, (LPWSTR) src,
					    src_size / sizeof (WCHAR),
					    0, 0, WCTOMB_INVALID_STRING, 0);
      Extbyte *outp = (Extbyte *) stack_like_malloc (nout);

      WideCharToMultiByte (cp, WCTOMB_OPTIONS, (LPWSTR) src,
			   src_size / sizeof (WCHAR), (LPSTR) outp, nout,
			   WCTOMB_INVALID_STRING, 0);
      *dst = (void *) outp;
      *dst_size = nout;
    }
}

Bytecount
unicode_multibyte_convert_size (const char *srctext, const void *src,
				Bytecount src_size, int to_unicode, int cp)
{
  alloca_convert_vals vals;
  
  int pos = find_pos_of_existing_active_alloca_convert (srctext);
  assert (pos < 0);

  vals.srctext = srctext;

  unicode_multibyte_convert_now_damn_it (src, src_size, to_unicode, &vals.dst,
					 &vals.dst_size, cp);

  Dynarr_add (active_alloca_convert, vals);
  return vals.dst_size;
}

void *
unicode_multibyte_convert_copy_data (const char *srctext, void *alloca_data)
{
  alloca_convert_vals *vals;
  int i = find_pos_of_existing_active_alloca_convert (srctext);

  assert (i >= 0);
  vals = Dynarr_atp (active_alloca_convert, i);
  assert (alloca_data);
  memcpy (alloca_data, vals->dst, vals->dst_size);
  stack_like_free (vals->dst);
  Dynarr_delete (active_alloca_convert, i);
  return alloca_data;
}

/* Convert multibyte to Unicode according to the specified code page
   and append the results onto the specified Dynarr.  See above. */

void
convert_multibyte_to_unicode_dynarr (const Extbyte *src, Bytecount n,
				     int cp, unsigned_char_dynarr *dst)
{
  Charcount nout = MultiByteToWideChar (cp, MBTOWC_OPTIONS, src, n, 0, 0);
  void *outp;

  Dynarr_add_many (dst, 0, nout * sizeof (WCHAR));
  /* dynarr's buffer may be realloc()ed by call above, so access it after */
  outp = Dynarr_atp (dst, Dynarr_length (dst) - nout * sizeof (WCHAR));
  MultiByteToWideChar (cp, MBTOWC_OPTIONS, src, n, (LPWSTR) outp, nout);
}

void
convert_unicode_to_multibyte_dynarr (const Extbyte *src, Bytecount n,
				     int cp, unsigned_char_dynarr *dst)
{
  Bytecount nout = WideCharToMultiByte (cp, WCTOMB_OPTIONS, (LPWSTR) src,
					n / sizeof (WCHAR),
					0, 0, WCTOMB_INVALID_STRING, 0);
  void *outp;

  Dynarr_add_many (dst, 0, nout);
  /* dynarr's buffer may be realloc()ed by call above, so access it after */
  outp = Dynarr_atp (dst, Dynarr_length (dst) - nout);
  WideCharToMultiByte (cp, WCTOMB_OPTIONS, (LPWSTR) src,
		       n / sizeof (WCHAR), (LPSTR) outp, nout,
		       WCTOMB_INVALID_STRING, 0);
}


/* Convert MS Windows multibyte to Unicode. */

static Bytecount
mswindows_multibyte_to_unicode_convert (struct coding_stream *str,
					const unsigned char *src,
					unsigned_char_dynarr *dst,
					Bytecount n)
{
  unsigned char *new_src = (unsigned char *) src;
  int i;
  struct mswindows_multibyte_to_unicode_coding_stream *data =
    CODING_STREAM_TYPE_DATA (str, mswindows_multibyte_to_unicode);
  Bytecount orign = n;

  if (data->cp == 0)
    data->cp = determine_code_page (str->codesys);
  if (data->partial_byte_present)
    {
      new_src = alloca_array (unsigned char, n + 1);
      memcpy (new_src + 1, src, n);
      new_src[0] =
	(unsigned char) data->partial_byte;
      n++;
    }

  if (str->direction == CODING_DECODE)
    {
      for (i = n - 1; i >= 0; i--)
	{
	  if (!IsDBCSLeadByteEx (data->cp, new_src[i]))
	    break;
	}

      i++;

      for (; i < n; i++)
	{
	  if (IsDBCSLeadByteEx (data->cp, new_src[i]))
	    i++;
	}

      if (i > n)
	{
	  /* a char is split across the boundary */
	  data->partial_byte = new_src[n - 1];
	  data->partial_byte_present = 1;
	  n--;
	}
      else
	data->partial_byte_present = 0;

      convert_multibyte_to_unicode_dynarr ((Extbyte *) new_src, n, data->cp,
					   dst);
    }
  else
    {
      if (n & 1)
	{
	  /* a char is split across the boundary */
	  data->partial_byte = new_src[n - 1];
	  data->partial_byte_present = 1;
	  n--;
	}
      else
	data->partial_byte_present = 0;

      convert_unicode_to_multibyte_dynarr ((Extbyte *) new_src, n, data->cp,
					   dst);
    }
  return orign;
}

static enum source_sink_type
mswindows_multibyte_to_unicode_conversion_end_type (Lisp_Object UNUSED (codesys))
{
  return DECODES_BYTE_TO_BYTE;
}


/************************************************************************/
/*                       MS Windows Multibyte methods                   */
/************************************************************************/

struct mswindows_multibyte_coding_system
{
  Lisp_Object code_page;
  Lisp_Object locale;
};

struct mswindows_multibyte_coding_stream
{
  int dummy;
};

static const struct memory_description
  mswindows_multibyte_coding_system_description[] = {
  { XD_LISP_OBJECT,
    offsetof (struct mswindows_multibyte_coding_system, code_page) },
  { XD_LISP_OBJECT,
    offsetof (struct mswindows_multibyte_coding_system, locale) },
  { XD_END }
};

DEFINE_CODING_SYSTEM_TYPE_WITH_DATA (mswindows_multibyte);

static Bytecount
mswindows_multibyte_convert (struct coding_stream *UNUSED (str),
			     const UExtbyte *UNUSED (src),
			     unsigned_char_dynarr *UNUSED (dst), Bytecount n)
{
  Bytecount orign = n;
  /* should never be called; is preprocessed away in the
     canonicalize method */
  ABORT ();
  return orign;
}

static void
mswindows_multibyte_init (Lisp_Object codesys)
{
  struct mswindows_multibyte_coding_system *data =
    XCODING_SYSTEM_TYPE_DATA (codesys, mswindows_multibyte);

  data->code_page = Qnil;
  data->locale = Qnil;
}

static void
mswindows_multibyte_mark (Lisp_Object codesys)
{
  struct mswindows_multibyte_coding_system *data =
    XCODING_SYSTEM_TYPE_DATA (codesys, mswindows_multibyte);

  mark_object (data->code_page);
  mark_object (data->locale);
}

static int
mswindows_multibyte_putprop (Lisp_Object codesys,
			     Lisp_Object key,
			     Lisp_Object value)
{
  struct mswindows_multibyte_coding_system *data =
    XCODING_SYSTEM_TYPE_DATA (codesys, mswindows_multibyte);

  if (EQ (key, Qcode_page))
    data->code_page = value;
  else if (EQ (key, Qlocale))
    data->locale = value;
  else
    return 0;
  return 1;
}

static Lisp_Object
mswindows_multibyte_getprop (Lisp_Object coding_system,
			     Lisp_Object prop)
{
  struct mswindows_multibyte_coding_system *data =
    XCODING_SYSTEM_TYPE_DATA (coding_system, mswindows_multibyte);

  if (EQ (prop, Qcode_page))
    return data->code_page;
  else if (EQ (prop, Qlocale))
    return data->locale;
  else
    return Qunbound;
}

/* Convert this coding system into the proper chain. */

static Lisp_Object
mswindows_multibyte_canonicalize (Lisp_Object codesys)
{
  struct mswindows_multibyte_coding_system *data =
    XCODING_SYSTEM_TYPE_DATA (codesys, mswindows_multibyte);
  Lisp_Object m2u;

  m2u =
    make_internal_coding_system
      (Qnil,
       "internal-mswindows-multibyte-to-unicode",
       Qmswindows_multibyte_to_unicode,
       Qnil, NILP (data->locale) ?
       list2 (Qcode_page, data->code_page) :
       list4 (Qcode_page, data->code_page, Qlocale, data->locale));

  return make_internal_coding_system (codesys,
				      "internal-mswindows-multibyte-chain",
				      Qchain, Qunbound,
				      list4 (Qchain,
					     list2 (m2u, Qmswindows_unicode),
					     Qcanonicalize_after_coding,
					     codesys));
}


void
syms_of_intl_win32 (void)
{
#ifdef MULE
  DEFSUBR (Fmswindows_set_current_locale);
  DEFSUBR (Fmswindows_current_locale);
  DEFSUBR (Fmswindows_user_default_locale);
  DEFSUBR (Fmswindows_system_default_locale);
  DEFSUBR (Fmswindows_locale_code_page);
  DEFSUBR (Fmswindows_locale_oem_code_page);
  DEFSUBR (Fmswindows_supported_locales);
  DEFSUBR (Fmswindows_charset_code_page);
  DEFSUBR (Fmswindows_set_charset_code_page);
  DEFSUBR (Fmswindows_charset_registry);
  DEFSUBR (Fmswindows_set_charset_registry);

#if 0
  DEFSUBR (Fmswindows_get_locale_info);
  DEFSUBR (Fmswindows_get_current_locale_id);
  DEFSUBR (Fmswindows_get_default_locale_id);
  DEFSUBR (Fmswindows_get_valid_locale_ids);
  DEFSUBR (Fmswindows_set_current_locale);

  DEFSUBR (Fmswindows_get_console_code_page);
  DEFSUBR (Fmswindows_set_console_code_page);
  DEFSUBR (Fmswindows_get_console_output_code_page);
  DEFSUBR (Fmswindows_set_console_output_code_page);
  DEFSUBR (Fmswindows_get_valid_code_pages);
  DEFSUBR (Fmswindows_get_code_page_charset);

  DEFSUBR (Fmswindows_get_valid_keyboard_layouts);
  DEFSUBR (Fmswindows_get_keyboard_layout);
  DEFSUBR (Fmswindows_set_keyboard_layout);
#endif
#endif /* MULE */

  DEFSYMBOL (Qmswindows_tstr);
  DEFSYMBOL (Qmswindows_multibyte);
  DEFSYMBOL (Qmswindows_multibyte_to_unicode);
  DEFSYMBOL (Qmswindows_unicode);
  DEFSYMBOL (Qmswindows_multibyte_system_default);

  DEFSYMBOL (Qansi);
  DEFSYMBOL (Qoem);
  DEFSYMBOL (Qmac);
  DEFSYMBOL (Qebcdic);
}

void
coding_system_type_create_intl_win32 (void)
{
  INITIALIZE_CODING_SYSTEM_TYPE_WITH_DATA
    (mswindows_multibyte_to_unicode,
     "mswindows-multibyte-to-unicode-coding-system-p");
  CODING_SYSTEM_HAS_METHOD (mswindows_multibyte_to_unicode, init);
  CODING_SYSTEM_HAS_METHOD (mswindows_multibyte_to_unicode, print);
  CODING_SYSTEM_HAS_METHOD (mswindows_multibyte_to_unicode, convert);
  CODING_SYSTEM_HAS_METHOD (mswindows_multibyte_to_unicode, getprop);
  CODING_SYSTEM_HAS_METHOD (mswindows_multibyte_to_unicode, putprop);
  CODING_SYSTEM_HAS_METHOD (mswindows_multibyte_to_unicode,
			    conversion_end_type);

  INITIALIZE_CODING_SYSTEM_TYPE_WITH_DATA
    (mswindows_multibyte,
     "mswindows-multibyte-coding-system-p");
  CODING_SYSTEM_HAS_METHOD (mswindows_multibyte, convert);
  CODING_SYSTEM_HAS_METHOD (mswindows_multibyte, init);
  CODING_SYSTEM_HAS_METHOD (mswindows_multibyte, mark);
  CODING_SYSTEM_HAS_METHOD (mswindows_multibyte, getprop);
  CODING_SYSTEM_HAS_METHOD (mswindows_multibyte, putprop);
  CODING_SYSTEM_HAS_METHOD (mswindows_multibyte, canonicalize);
}

void
reinit_coding_system_type_create_intl_win32 (void)
{
  REINITIALIZE_CODING_SYSTEM_TYPE (mswindows_multibyte_to_unicode);
  REINITIALIZE_CODING_SYSTEM_TYPE (mswindows_multibyte);
}

void
vars_of_intl_win32 (void)
{
#ifdef MULE
  Vmswindows_charset_code_page_table =
    make_lisp_hash_table (50, HASH_TABLE_NON_WEAK, Qeq);
  staticpro (&Vmswindows_charset_code_page_table);
  Vmswindows_charset_registry_table =
    make_lisp_hash_table (50, HASH_TABLE_NON_WEAK, Qeq);
  staticpro (&Vmswindows_charset_registry_table);
#endif /* MULE */
}

static void
determine_if_using_unicode (void)
{
  if (XEUNICODE_P)
    Fdefine_coding_system_alias (Qmswindows_tstr, Qmswindows_unicode);
  else
    Fdefine_coding_system_alias (Qmswindows_tstr,
				 Qmswindows_multibyte_system_default);
}

void
complex_vars_of_intl_win32 (void)
{
  Fmake_coding_system_internal
    (Qmswindows_unicode, Qunicode,
     build_defer_string ("MS Windows Unicode"),
     listu (Qdocumentation,
            build_defer_string (
"Converts to the Unicode encoding for Windows API calls.\n"
"This encoding is equivalent to standard UTF16, little-endian."
),
            Qmnemonic, build_ascstring ("MSW-U"),
            Qunicode_type, Qutf_16,
            Qlittle_endian, Qt,
            Qunbound));

#ifdef MULE
  /* Just temporarily.  This will get fixed in mule-msw-init.el. */
  Fdefine_coding_system_alias (Qmswindows_multibyte_system_default,
			       Qraw_text);
#else
  /* Not temporarily.  These may be referenced by Lisp code so we need to
     define them. */
  Fdefine_coding_system_alias (Qmswindows_multibyte,
			       Qraw_text);
  Fdefine_coding_system_alias (Qmswindows_multibyte_system_default,
			       Qraw_text);
  Fdefine_coding_system_alias (intern ("mswindows-multibyte-user-default"),
			       Qraw_text);
  Fdefine_coding_system_alias (intern ("mswindows-multibyte-oem"),
			       Qraw_text);
  Fdefine_coding_system_alias (intern
			       ("mswindows-multibyte-oem-system-default"),
			       Qraw_text);
  Fdefine_coding_system_alias (intern ("mswindows-multibyte-oem-user-default"),
			       Qraw_text);
#endif /* MULE */

  determine_if_using_unicode ();
}

void
init_intl_win32 (void)
{
#ifdef MULE
  set_current_lcid (GetUserDefaultLCID ());
#endif /* MULE */

#ifdef HAVE_CYGWIN_CONV_PATH
  Fprovide (intern ("cygwin-use-utf-8"));
#endif

  if (initialized)
    /* If not initialized, we also call this, but early -- see the
       previous function. */
    determine_if_using_unicode ();
}
