- Implemented setlocale parsing and LC_TYPE behavior.
- Implemented isleadbyte, snprintf.
- Added NLS IsValidCodePage prototype, misc CRTDLL fixes.

diff --git a/dlls/crtdll/Makefile.in b/dlls/crtdll/Makefile.in
index a8fbe3c..10b626b 100644
--- a/dlls/crtdll/Makefile.in
+++ b/dlls/crtdll/Makefile.in
@@ -13,6 +13,7 @@
 	dir.c \
 	exit.c \
 	file.c \
+	locale.c \
 	mbstring.c \
 	memory.c \
 	spawn.c \
diff --git a/dlls/crtdll/crtdll.h b/dlls/crtdll/crtdll.h
index 867920c..30bfc6b 100644
--- a/dlls/crtdll/crtdll.h
+++ b/dlls/crtdll/crtdll.h
@@ -19,20 +19,20 @@
 #define CRTDLL_LC_MONETARY	3
 #define CRTDLL_LC_NUMERIC	4
 #define CRTDLL_LC_TIME		5
-#define CRTDLL_LC_MIN		LC_ALL
-#define CRTDLL_LC_MAX		LC_TIME
+#define CRTDLL_LC_MIN		CRTDLL_LC_ALL
+#define CRTDLL_LC_MAX		CRTDLL_LC_TIME
 
 /* ctype defines */
-#define CRTDLL_UPPER		0x1
-#define CRTDLL_LOWER		0x2
-#define CRTDLL_DIGIT		0x4
-#define CRTDLL_SPACE		0x8
-#define CRTDLL_PUNCT		0x10
-#define CRTDLL_CONTROL		0x20
-#define CRTDLL_BLANK		0x40
-#define CRTDLL_HEX		0x80
+#define CRTDLL_UPPER		C1_UPPER
+#define CRTDLL_LOWER		C1_LOWER
+#define CRTDLL_DIGIT		C1_DIGIT
+#define CRTDLL_SPACE		C1_SPACE
+#define CRTDLL_PUNCT		C1_PUNCT
+#define CRTDLL_CONTROL		C1_CNTRL
+#define CRTDLL_BLANK		C1_BLANK
+#define CRTDLL_HEX		C1_XDIGIT
 #define CRTDLL_LEADBYTE		0x8000
-#define CRTDLL_ALPHA		(0x0100|CRTDLL_UPPER|CRTDLL_LOWER)
+#define CRTDLL_ALPHA		(C1_ALPHA|CRTDLL_UPPER|CRTDLL_LOWER)
 
 /* stat() mode bits */
 #define _S_IFMT                  0170000
@@ -416,6 +416,7 @@
 double __cdecl CRTDLL__y0( double x );
 double __cdecl CRTDLL__y1( double x );
 double __cdecl CRTDLL__yn( INT x, double y );
+double __cdecl CRTDLL__nextafter( double x, double y );
 
 /* CRTDLL_mem.c */
 LPVOID  __cdecl CRTDLL_new( DWORD size );
@@ -487,4 +488,10 @@
 LPSTR  __CRTDLL__strndup(LPSTR buf, INT size);
 VOID   __CRTDLL__init_io(VOID);
 
+extern WORD CRTDLL_ctype [257];
+extern WORD __CRTDLL_current_ctype[257];
+extern WORD* CRTDLL_pctype_dll;
+extern INT CRTDLL__mb_cur_max_dll;
+extern LCID __CRTDLL_current_lc_all_lcid;
+
 #endif /* __WINE_CRTDLL_H */
diff --git a/dlls/crtdll/crtdll.spec b/dlls/crtdll/crtdll.spec
index 9d78ae8..2327d32 100644
--- a/dlls/crtdll/crtdll.spec
+++ b/dlls/crtdll/crtdll.spec
@@ -38,7 +38,7 @@
 @ cdecl __isascii(long) CRTDLL___isascii
 @ cdecl __iscsym(long) CRTDLL___iscsym
 @ cdecl __iscsymf(long) CRTDLL___iscsymf
-@ stub __mb_cur_max_dll
+@ extern __mb_cur_max_dll CRTDLL__mb_cur_max_dll
 @ stub __pxcptinfoptrs
 @ forward __threadhandle kernel32.GetCurrentThread
 @ forward __threadid kernel32.GetCurrentThreadId
@@ -244,7 +244,7 @@
 @ cdecl _mkdir(str) CRTDLL__mkdir
 @ cdecl _mktemp(str) CRTDLL__mktemp
 @ cdecl _msize(ptr) CRTDLL__msize
-@ cdecl _nextafter(double double) nextafter
+@ cdecl _nextafter(double double) CRTDLL__nextafter
 @ cdecl _onexit(ptr) CRTDLL__onexit
 @ cdecl _open(str long) CRTDLL__open
 @ cdecl _open_osfhandle(long long) CRTDLL__open_osfhandle
@@ -254,7 +254,7 @@
 @ extern _osver_dll CRTDLL_osver_dll
 @ extern _osversion_dll CRTDLL_osversion_dll
 @ stub _pclose
-@ stub _pctype_dll
+@ extern _pctype_dll CRTDLL_pctype_dll
 @ stub _pgmptr_dll
 @ stub _pipe
 @ stub _popen
@@ -275,7 +275,7 @@
 @ cdecl _setmode(long long) CRTDLL__setmode
 @ stub _setsystime
 @ cdecl _sleep(long) CRTDLL__sleep
-@ stub _snprintf
+@ varargs _snprintf(ptr long ptr) snprintf
 @ stub _snwprintf
 @ stub _sopen
 @ stub _spawnl
@@ -406,7 +406,7 @@
 @ cdecl iscntrl(long) CRTDLL_iscntrl
 @ cdecl isdigit(long) CRTDLL_isdigit
 @ cdecl isgraph(long) CRTDLL_isgraph
-@ stub isleadbyte
+@ cdecl isleadbyte(long) CRTDLL_isleadbyte
 @ cdecl islower(long) CRTDLL_islower
 @ cdecl isprint(long) CRTDLL_isprint
 @ cdecl ispunct(long) CRTDLL_ispunct
diff --git a/dlls/crtdll/crtdll_main.c b/dlls/crtdll/crtdll_main.c
index 5a01d2d..cdf2e88 100644
--- a/dlls/crtdll/crtdll_main.c
+++ b/dlls/crtdll/crtdll_main.c
@@ -72,6 +72,7 @@
 UINT CRTDLL_winver_dll;       /* CRTDLL.331 */
 INT  CRTDLL_doserrno = 0; 
 INT  CRTDLL_errno = 0;
+INT  CRTDLL__mb_cur_max_dll = 1;
 const INT  CRTDLL__sys_nerr = 43;
 
 /* ASCII char classification flags - binary compatible */
@@ -103,6 +104,17 @@
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
 };
 
+/* Internal: Current ctype table for locale */
+WORD __CRTDLL_current_ctype[257];
+ 
+/* pctype is used by macros in the Win32 headers. It must point
+ * To a table of flags exactly like ctype. To allow locale
+ * changes to affect ctypes (i.e. isleadbyte), we use a second table
+ * and update its flags whenever the current locale changes.
+ */
+WORD* CRTDLL_pctype_dll = __CRTDLL_current_ctype + 1;
+
+
 /*********************************************************************
  *                  CRTDLL_MainInit  (CRTDLL.init)
  */
@@ -112,6 +124,7 @@
 
 	if (fdwReason == DLL_PROCESS_ATTACH) {
 	  __CRTDLL__init_io();
+	  CRTDLL_setlocale( CRTDLL_LC_ALL, "C" );
 	  CRTDLL_HUGE_dll = HUGE_VAL;
 	}
 	return TRUE;
@@ -641,32 +654,29 @@
 
 
 /*********************************************************************
- *                  setlocale           (CRTDLL.453)
- */
-LPSTR __cdecl CRTDLL_setlocale(INT category,LPCSTR locale)
-{
-    LPSTR categorystr;
-
-    switch (category) {
-    case CRTDLL_LC_ALL: categorystr="LC_ALL";break;
-    case CRTDLL_LC_COLLATE: categorystr="LC_COLLATE";break;
-    case CRTDLL_LC_CTYPE: categorystr="LC_CTYPE";break;
-    case CRTDLL_LC_MONETARY: categorystr="LC_MONETARY";break;
-    case CRTDLL_LC_NUMERIC: categorystr="LC_NUMERIC";break;
-    case CRTDLL_LC_TIME: categorystr="LC_TIME";break;
-    default: categorystr = "UNKNOWN?";break;
-    }
-    FIXME("(%s,%s),stub!\n",categorystr,locale);
-    return "C";
-}
-
-
-/*********************************************************************
  *                  _isctype           (CRTDLL.138)
  */
-INT __cdecl CRTDLL__isctype(INT c,UINT type)
+INT __cdecl CRTDLL__isctype(INT c, UINT type)
 {
-  return CRTDLL_ctype[(UINT)c+1] & type;
+  if (c >= -1 && c <= 255)
+    return CRTDLL_pctype_dll[c] & type;
+
+  if (CRTDLL__mb_cur_max_dll != 1 && c > 0)
+  {
+    /* FIXME: Is there a faster way to do this? */
+    WORD typeInfo;
+    char convert[3], *pconv = convert;
+
+    if (CRTDLL_pctype_dll[(UINT)c >> 8] & CRTDLL_LEADBYTE)
+      *pconv++ = (UINT)c >> 8;
+    *pconv++ = c & 0xff;
+    *pconv = 0;
+    /* FIXME: Use ctype LCID */
+    if (GetStringTypeExA(__CRTDLL_current_lc_all_lcid, CT_CTYPE1,
+                         convert, convert[1] ? 2 : 1, &typeInfo))
+      return typeInfo & type;
+  }
+  return 0;
 }
 
 
@@ -997,6 +1007,15 @@
 
 
 /*********************************************************************
+ *                   isleadbyte         (CRTDLL.447)
+ */
+INT __cdecl CRTDLL_isleadbyte(unsigned char c)
+{
+    return CRTDLL__isctype( c, CRTDLL_LEADBYTE );
+}
+
+
+/*********************************************************************
  *                  islower          (CRTDLL.447)
  */
 INT __cdecl CRTDLL_islower(INT c)
@@ -1639,3 +1658,17 @@
   }
   return retVal;
 }
+
+
+/*********************************************************************
+ *                  _nextafter        (CRTDLL.235)
+ *
+ */
+double __cdecl CRTDLL__nextafter(double x, double y)
+{
+  double retVal;
+  if (!finite(x) || !finite(y)) CRTDLL_errno = EDOM;
+    retVal  = nextafter(x,y);
+  return retVal;
+}
+
diff --git a/dlls/crtdll/locale.c b/dlls/crtdll/locale.c
new file mode 100644
index 0000000..b99c80c
--- /dev/null
+++ b/dlls/crtdll/locale.c
@@ -0,0 +1,447 @@
+/*
+ * CRT Locale functions
+ *
+ * Copyright 2000 Jon Griffiths
+ *
+ * NOTES:
+ * Currently only LC_CTYPE behaviour is actually implemented.
+ * Passing a code page only is not yet supported.
+ * 
+ * The code maps a (potentially incomplete) locale description to
+ * an LCID. The algorithm enumerates supported locales and
+ * compares the locale strings to the locale information given.
+ * Fully qualified locales should be completely compatable.
+ * Some countries (e.g. US) have synonyms that can be used in
+ * setlocale() calls - these are mapped to ISO codes before
+ * searching begins, but I may have missed some out of the list.
+ *
+ * It should be noted that the algorithm may locate a valid
+ * locale from a 2 letter ISO code, while the real DLL won't
+ * (it requires 3 letter codes or synonyms at a minimum).
+ * e.g. setlocale(LC_ALL,"de") will return "German_Germany.1252"
+ * with this implementation, while this fails in win32.
+ *
+ * It should also be noted that this implementation follows
+ * the MSVCRT behaviour, and not the CRTDLL behaviour.
+ * This is because MSVCRT provides a superset of the CRTDLL
+ * allowed locales, so this code can be used for both. Also
+ * The CRTDLL implementation can be considered broken.
+ *
+ * The code currently works for isleadbyte() but will fail
+ * (produce potentially incorrect values) for other locales
+ * with isalpha() etc. This is because the current Wine
+ * implementation of GetStringTypeA() is not locale aware.
+ * Fixing this requires a table of which characters in the
+ * code page are upper/lower/digit etc. If you locate such
+ * a table for a supported Wine locale, mail it to me and
+ * I will add the needed support (jon_p_griffiths@yahoo.com).
+ */
+#include "crtdll.h"
+#include <winnt.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+DEFAULT_DEBUG_CHANNEL(crtdll);
+
+#define MAX_ELEM_LEN 64 /* Max length of country/language/CP string */
+#define MAX_LOCALE_LENGTH 256
+
+/* FIXME: Need to hold locale for each LC_* type and aggregate
+ * string to produce lc_all.
+ */
+char __CRTDLL_current_lc_all[MAX_LOCALE_LENGTH];
+LCID __CRTDLL_current_lc_all_lcid;
+
+/* Friendly country strings & iso codes for synonym support.
+ * Based on MS documentation for setlocale().
+ */
+static const char* _country_synonyms[] = 
+{
+  "Hong Kong","HK",
+  "Hong-Kong","HK",
+  "New Zealand","NZ",
+  "New-Zealand","NZ",
+  "PR China","CN",
+  "PR-China","CN",
+  "United Kingdom","GB",
+  "United-Kingdom","GB",
+  "Britain","GB",
+  "England","GB",
+  "Great Britain","GB",
+  "United States","US",
+  "United-States","US",
+  "America","US"
+};
+
+/* INTERNAL: Map a synonym to an ISO code */
+static void remap_synonym(char *name)
+{
+  int i;
+  for (i = 0; i < sizeof(_country_synonyms)/sizeof(char*); i += 2 )
+  {
+    if (!strcasecmp(_country_synonyms[i],name))
+    {
+      TRACE(":Mapping synonym %s to %s\n",name,_country_synonyms[i+1]);
+      name[0] = _country_synonyms[i+1][0];
+      name[1] = _country_synonyms[i+1][1];
+      name[2] = '\0';
+      return;
+    }
+  }
+}
+
+/* Note: Flags are weighted in order of matching importance */
+#define FOUND_LANGUAGE         0x4
+#define FOUND_COUNTRY          0x2
+#define FOUND_CODEPAGE         0x1
+
+typedef struct {
+  char search_language[MAX_ELEM_LEN];
+  char search_country[MAX_ELEM_LEN];
+  char search_codepage[MAX_ELEM_LEN];
+  char found_language[MAX_ELEM_LEN];
+  char found_country[MAX_ELEM_LEN];
+  char found_codepage[MAX_ELEM_LEN];
+  unsigned int match_flags;
+  LANGID found_lang_id;
+} locale_search_t;
+
+#define CONTINUE_LOOKING TRUE
+#define STOP_LOOKING     FALSE
+
+/* INTERNAL: Get and compare locale info with a given string */
+static int compare_info(LCID lcid, DWORD flags, char* buff, const char* cmp)
+{
+  buff[0] = 0;
+  GetLocaleInfoA(lcid, flags|LOCALE_NOUSEROVERRIDE,buff, MAX_ELEM_LEN);
+  if (!buff[0] || !cmp[0])
+    return 0;
+  /* Partial matches are allowed, e.g. "Germ" matches "Germany" */
+  return !strncasecmp(cmp, buff, strlen(cmp));
+}
+
+
+/* INTERNAL: Callback for enumerated languages */
+static BOOL CALLBACK
+find_best_locale_proc(HMODULE hModule, LPCSTR type,
+                      LPCSTR name, WORD LangID, LONG lParam)
+{
+  locale_search_t *res = (locale_search_t *)lParam;
+  const LCID lcid = MAKELCID(LangID, SORT_DEFAULT);
+  char buff[MAX_ELEM_LEN];
+  unsigned int flags = 0;
+
+  if(PRIMARYLANGID(LangID) == LANG_NEUTRAL)
+    return CONTINUE_LOOKING;
+  
+  /* Check Language */
+  if (compare_info(lcid,LOCALE_SISO639LANGNAME,buff,res->search_language) ||
+      compare_info(lcid,LOCALE_SABBREVLANGNAME,buff,res->search_language) ||
+      compare_info(lcid,LOCALE_SENGLANGUAGE,buff,res->search_language))
+  {
+    TRACE(":Found language: %s->%s\n", res->search_language, buff);
+    flags |= FOUND_LANGUAGE;
+    memcpy(res->found_language,res->search_language,MAX_ELEM_LEN);
+  }
+  else if (res->match_flags & FOUND_LANGUAGE)
+  {
+    return CONTINUE_LOOKING;
+  }
+
+  /* Check Country */
+  if (compare_info(lcid,LOCALE_SISO3166CTRYNAME,buff,res->search_country) ||
+      compare_info(lcid,LOCALE_SABBREVCTRYNAME,buff,res->search_country) ||
+      compare_info(lcid,LOCALE_SENGCOUNTRY,buff,res->search_country))
+  {
+    TRACE("Found country:%s->%s\n", res->search_country, buff);
+    flags |= FOUND_COUNTRY;
+    memcpy(res->found_country,res->search_country,MAX_ELEM_LEN);
+  }
+  else if (res->match_flags & FOUND_COUNTRY)
+  {
+    return CONTINUE_LOOKING;
+  }
+
+  /* Check codepage */
+  if (compare_info(lcid,LOCALE_IDEFAULTCODEPAGE,buff,res->search_codepage) ||
+      (compare_info(lcid,LOCALE_IDEFAULTANSICODEPAGE,buff,res->search_codepage)))
+  {
+    TRACE("Found codepage:%s->%s\n", res->search_codepage, buff);
+    flags |= FOUND_CODEPAGE;
+    memcpy(res->found_codepage,res->search_codepage,MAX_ELEM_LEN);
+  }
+  else if (res->match_flags & FOUND_CODEPAGE)
+  {
+    return CONTINUE_LOOKING;
+  }
+
+  if (flags > res->match_flags)
+  {
+    /* Found a better match than previously */
+    res->match_flags = flags;
+    res->found_lang_id = LangID;
+  }
+  if (flags & (FOUND_LANGUAGE & FOUND_COUNTRY & FOUND_CODEPAGE))
+  {
+    TRACE(":found exact locale match\n");
+    return STOP_LOOKING;
+  }
+  return CONTINUE_LOOKING;
+}
+
+/* Internal: Find the LCID for a locale specification */
+static LCID __CRTDLL_locale_to_LCID(locale_search_t* locale)
+{
+  LCID lcid;
+  EnumResourceLanguagesA(GetModuleHandleA("KERNEL32"), RT_STRINGA,
+			 (LPCSTR)LOCALE_ILANGUAGE,find_best_locale_proc,
+			 (LONG)locale);
+
+  if (!locale->match_flags)
+    return 0;
+
+  /* If we were given something that didn't match, fail */
+  if (locale->search_country[0] && !(locale->match_flags & FOUND_COUNTRY))
+    return 0;
+
+  lcid =  MAKELCID(locale->found_lang_id, SORT_DEFAULT);
+
+  /* Populate partial locale, translating LCID to locale string elements */
+  if (!locale->found_codepage[0])
+  {
+    /* Even if a codepage is not enumerated for a locale
+     * it can be set if valid */
+    if (locale->search_codepage[0])
+    {
+      if (IsValidCodePage(atoi(locale->search_codepage)))
+        memcpy(locale->found_codepage,locale->search_codepage,MAX_ELEM_LEN);
+      else
+      {
+        /* Special codepage values: OEM & ANSI */
+        if (strcasecmp(locale->search_codepage,"OCP"))
+        {
+          GetLocaleInfoA(lcid, LOCALE_IDEFAULTCODEPAGE,
+                         locale->found_codepage, MAX_ELEM_LEN);
+        }
+        if (strcasecmp(locale->search_codepage,"ACP"))
+        {
+          GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE,
+                         locale->found_codepage, MAX_ELEM_LEN);
+        }
+        else
+          return 0;
+
+        if (!atoi(locale->found_codepage))
+           return 0;
+      }
+    }
+    else
+    {
+      /* Prefer ANSI codepages if present */
+      GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE,
+                     locale->found_codepage, MAX_ELEM_LEN);
+      if (!locale->found_codepage[0] || !atoi(locale->found_codepage))
+          GetLocaleInfoA(lcid, LOCALE_IDEFAULTCODEPAGE,
+                         locale->found_codepage, MAX_ELEM_LEN);
+    }
+  }
+  GetLocaleInfoA(lcid, LOCALE_SENGLANGUAGE|LOCALE_NOUSEROVERRIDE,
+                 locale->found_language, MAX_ELEM_LEN);
+  GetLocaleInfoA(lcid, LOCALE_SENGCOUNTRY|LOCALE_NOUSEROVERRIDE,
+                 locale->found_country, MAX_ELEM_LEN);
+  return lcid;
+}
+
+
+/* INTERNAL: Set ctype behaviour for a codepage */
+static void __CRTDLL_set_ctype(UINT codepage, LCID lcid)
+{
+  CPINFO cp;
+
+  memset(&cp, 0, sizeof(CPINFO));
+
+  if (GetCPInfo(codepage, &cp))
+  {
+    int i;
+    char str[3];
+    unsigned char *traverse = (unsigned char *)cp.LeadByte;
+
+    memset(__CRTDLL_current_ctype, 0, sizeof(CRTDLL_ctype));
+
+    /* Switch ctype macros to MBCS if needed */
+    CRTDLL__mb_cur_max_dll = cp.MaxCharSize;
+
+    /* Set remaining ctype flags: FIXME: faster way to do this? */
+    str[1] = str[2] = 0;
+    for (i = 0; i < 256; i++)
+    {
+      if (!(CRTDLL_pctype_dll[i] & CRTDLL_LEADBYTE))
+      {
+        str[0] = i;
+        GetStringTypeA(lcid, CT_CTYPE1, str, 1, CRTDLL_pctype_dll + i);
+      }
+    }
+
+    /* Set leadbyte flags */
+    while (traverse[0] || traverse[1])
+    {
+      for( i = traverse[0]; i <= traverse[1]; i++ )
+        __CRTDLL_current_ctype[i+1] |= CRTDLL_LEADBYTE;
+      traverse += 2;
+    };
+  }
+}
+
+
+/*********************************************************************
+ *                  setlocale           (CRTDLL.453)
+ */
+LPSTR __cdecl CRTDLL_setlocale(INT category, LPCSTR locale)
+{
+  LCID lcid = 0;
+  locale_search_t lc;
+  int haveLang, haveCountry, haveCP;
+  char* next;
+  int lc_all = 0;
+
+  if (category < CRTDLL_LC_MIN || category > CRTDLL_LC_MAX)
+    return NULL;
+
+  if (locale == NULL)
+  {
+    /* Report the current Locale */
+    return __CRTDLL_current_lc_all;
+  }
+
+  if (locale[0] == 'L' && locale[1] == 'C' && locale[2] == '_')
+  {
+    FIXME(":restore previous locale not implemented!\n");
+    /* FIXME: Easiest way to do this is parse the string and
+     * call this function recursively with its elements,
+     * Where they differ for each lc_ type.
+     */
+    return __CRTDLL_current_lc_all;
+  }
+
+  /* Default Locale: Special case handling */
+  if (!strlen(locale) || ((toupper(locale[0]) == 'C') && !locale[1]))
+  {
+    if ((toupper(__CRTDLL_current_lc_all[0]) != 'C')
+        || __CRTDLL_current_lc_all[1])
+    {
+      __CRTDLL_current_lc_all[0] = 'C';
+      __CRTDLL_current_lc_all[1] = 0;
+      switch (category) {
+      case CRTDLL_LC_ALL:
+        lc_all = 1; /* Fall through all cases ... */
+      case CRTDLL_LC_COLLATE:
+        if (!lc_all) break;
+      case CRTDLL_LC_CTYPE:
+        /* Restore C locale ctype info */
+        CRTDLL__mb_cur_max_dll = 1;
+        memcpy(__CRTDLL_current_ctype, CRTDLL_ctype, sizeof(CRTDLL_ctype));
+        if (!lc_all) break;
+      case CRTDLL_LC_MONETARY:
+        if (!lc_all) break;
+      case CRTDLL_LC_NUMERIC:
+        if (!lc_all) break;
+      case CRTDLL_LC_TIME:
+      }
+      return __CRTDLL_current_lc_all;
+    }
+  }
+
+  /* Get locale elements */
+  haveLang = haveCountry = haveCP = 0;
+  memset(&lc,0,sizeof(lc));
+
+  next = strchr(locale,'_');
+  if (next && next != locale)
+  {
+    haveLang = 1;
+    strncpy(lc.search_language,locale,next-locale);
+    locale += next-locale+1;
+  }
+
+  next = strchr(locale,'.');
+  if (next)
+  {
+    haveCP = 1;
+    if (next == locale)
+    {
+      locale++;
+      strncpy(lc.search_codepage, locale, MAX_ELEM_LEN);
+    }
+    else
+    {
+      if (haveLang)
+      {
+        haveCountry = 1;
+        strncpy(lc.search_country,locale,next-locale);
+        locale += next-locale+1;
+      }
+      else
+      {
+        haveLang = 1;
+        strncpy(lc.search_language,locale,next-locale);
+        locale += next-locale+1;
+      }
+      strncpy(lc.search_codepage, locale, MAX_ELEM_LEN);
+    }
+  }
+  else
+  {
+    if (haveLang)
+    {
+      haveCountry = 1;
+      strncpy(lc.search_country, locale, MAX_ELEM_LEN);
+    }
+    else
+    {
+      haveLang = 1;
+      strncpy(lc.search_language, locale, MAX_ELEM_LEN);
+    }
+  }
+
+  if (haveCountry)
+    remap_synonym(lc.search_country);
+
+  if (haveCP && !haveCountry && !haveLang)
+  {
+    FIXME(":Codepage only locale not implemented");
+    /* FIXME: Use default lang/country and skip locale_to_LCID()
+     * call below...
+     */
+    return NULL;
+  }
+
+  lcid = __CRTDLL_locale_to_LCID(&lc);
+
+  TRACE(":found LCID %ld\n",lcid);
+
+  if (lcid == 0)
+    return NULL;
+
+  __CRTDLL_current_lc_all_lcid = lcid;
+
+  snprintf(__CRTDLL_current_lc_all,MAX_LOCALE_LENGTH,"%s_%s.%s",
+	   lc.found_language,lc.found_country,lc.found_codepage);
+
+    switch (category) {
+    case CRTDLL_LC_ALL: 
+      lc_all = 1; /* Fall through all cases ... */
+    case CRTDLL_LC_COLLATE:
+      if (!lc_all) break;
+    case CRTDLL_LC_CTYPE:
+      __CRTDLL_set_ctype(atoi(lc.found_codepage),lcid);
+      if (!lc_all) break;
+      break;
+    case CRTDLL_LC_MONETARY:
+      if (!lc_all) break;
+    case CRTDLL_LC_NUMERIC:
+      if (!lc_all) break;
+    case CRTDLL_LC_TIME:
+    }
+    return __CRTDLL_current_lc_all;
+}
diff --git a/dlls/crtdll/time.c b/dlls/crtdll/time.c
index 4e6744b..1d72eba 100644
--- a/dlls/crtdll/time.c
+++ b/dlls/crtdll/time.c
@@ -84,7 +84,7 @@
     times(&alltimes);
     res = alltimes.tms_utime + alltimes.tms_stime+
         alltimes.tms_cutime + alltimes.tms_cstime;
-    /* Fixme: We need some symbolic representation
+    /* FIXME: We need some symbolic representation
        for (Hostsystem_)CLOCKS_PER_SEC 
        and (Emulated_system_)CLOCKS_PER_SEC
        10 holds only for Windows/Linux_i86)
diff --git a/include/winnls.h b/include/winnls.h
index b4307c7..1ffdd3c 100644
--- a/include/winnls.h
+++ b/include/winnls.h
@@ -344,6 +344,7 @@
 
 LCID WINAPI ConvertDefaultLocale(LCID   Locale);
 
+BOOL    WINAPI IsValidCodePage(UINT);
 BOOL    WINAPI GetCPInfo(UINT,LPCPINFO);
 BOOL    WINAPI GetCPInfoExA(UINT,DWORD,LPCPINFOEXA);
 BOOL    WINAPI GetCPInfoExW(UINT,DWORD,LPCPINFOEXW);