Index: trunk/src/emx/src/lib/locale/__convcp.c
===================================================================
--- trunk/src/emx/src/lib/locale/__convcp.c	(revision 496)
+++ trunk/src/emx/src/lib/locale/__convcp.c	(revision 496)
@@ -0,0 +1,46 @@
+/*
+    Locale support implementation through OS/2 Unicode API.
+    Copyright (c) 2003 InnoTek Systemberatung GmbH
+
+    For conditions of distribution and use, see the file COPYING.
+
+    Convert an ASCIIZ codepage to UCS-2 form and convert POSIX codepage
+    names to names understood by OS/2 Unicode API.
+*/
+
+#include <string.h>
+#include <uconv.h>
+
+/* Convert an encoding name to te form understood by UniCreateUconvObject.  */
+void
+__convert_codepage (const char *cp, UniChar *ucp)
+{
+  size_t sl = 0;
+
+  if (!stricmp (cp, "EUC-JP"))
+    memcpy (ucp, L"IBM-954", 8*2);
+  else if (!stricmp (cp, "EUC-KR"))
+    memcpy (ucp, L"IBM-970", 8*2);
+  else if (!stricmp (cp, "EUC-TW"))
+    memcpy (ucp, L"IBM-964", 8*2);
+  else if (!stricmp (cp, "EUC-CN"))
+    memcpy (ucp, L"IBM-1383", 9*2);
+  else if (!stricmp (cp, "BIG5"))
+    memcpy (ucp, L"IBM-950", 8*2);
+  else
+    {
+      /* Transform CPXXX naming style to IBM-XXX style */
+      if ((cp[0] == 'C' || cp[0] == 'c') && (cp[1] == 'P' || cp[1] == 'p'))
+        {
+          ucp[sl++] = 'I';
+          ucp[sl++] = 'B';
+          ucp[sl++] = 'M';
+          ucp[sl++] = '-';
+          cp += 2;
+        }
+
+      while (*cp != '\0')
+        ucp[sl++] = *cp++;
+      ucp[sl] = 0;
+    }
+}
Index: trunk/src/emx/src/lib/locale/__do_uni.c
===================================================================
--- trunk/src/emx/src/lib/locale/__do_uni.c	(revision 496)
+++ trunk/src/emx/src/lib/locale/__do_uni.c	(revision 496)
@@ -0,0 +1,55 @@
+/*
+    Locale support implementation through OS/2 Unicode API.
+    Copyright (c) 2003 InnoTek Systemberatung GmbH
+
+    For conditions of distribution and use, see the file COPYING.
+
+    Utility routine that converts a string to Unicode, calls some
+    function and then converts the string back. Used to handle
+    MBCS encodings.
+*/
+
+#include <uconv.h>
+#include <alloca.h>
+#include <malloc.h>
+#include <string.h>
+
+void __do_Unicode (UconvObject *uconv, char *s, void *arg,
+  int (*xform) (UniChar *, void *))
+{
+  /* We'll get maximum that much UniChar's */
+  size_t sl = strlen (s) + 1;
+  char free_ucs = 0;
+  UniChar *ucs;
+  void *mbcsbuf;
+  UniChar *ucsbuf;
+  size_t in_left, out_left, nonid;
+
+  /* Allocate strings up to 2000 characters on the stack */
+  if (sl < 4096 / sizeof (UniChar))
+    ucs = alloca (sl * sizeof (UniChar));
+  else
+  {
+    free_ucs = 1;
+    ucs = malloc (sl * sizeof (UniChar));
+  }
+
+  /* Translate the string to Unicode */
+  mbcsbuf = s; in_left = sl;
+  ucsbuf = ucs; out_left = sl;
+  if (UniUconvToUcs (uconv, &mbcsbuf, &in_left, &ucsbuf, &out_left, &nonid))
+    goto out;
+
+  /* Apply the transform function */
+  if (xform (ucs, arg))
+  {
+    /* Now convert back to MBCS */
+    ucsbuf = ucs; in_left = sl - out_left;
+    mbcsbuf = s; out_left = sl;
+    UniUconvFromUcs (uconv, &ucsbuf, &in_left, &mbcsbuf, &out_left, &nonid);
+  }
+
+out:
+  if (free_ucs)
+    free (ucs);
+}
Index: trunk/src/emx/src/lib/locale/__from_ucs.c
===================================================================
--- trunk/src/emx/src/lib/locale/__from_ucs.c	(revision 496)
+++ trunk/src/emx/src/lib/locale/__from_ucs.c	(revision 496)
@@ -0,0 +1,25 @@
+/*
+    Locale support implementation through OS/2 Unicode API.
+    Copyright (c) 2003 InnoTek Systemberatung GmbH
+
+    For conditions of distribution and use, see the file COPYING.
+
+    Helper function: Convert a character from Unicode.
+    Returns number of bytes in the MBCS character.
+*/
+
+#include <uconv.h>
+
+int __from_ucs (UconvObject uconv_obj, UniChar c, unsigned char *sbcs,
+  size_t len)
+{
+  UniChar *inbuf = &c;
+  void *outbuf = sbcs;
+  size_t nonid, in_left = 1, out_left = len;
+
+  if (UniUconvFromUcs (uconv_obj, &inbuf, &in_left, &outbuf, &out_left, &nonid)
+   || nonid || in_left)
+    return 0;
+
+  return len - out_left;
+}
Index: trunk/src/emx/src/lib/locale/__to_ucs.c
===================================================================
--- trunk/src/emx/src/lib/locale/__to_ucs.c	(revision 496)
+++ trunk/src/emx/src/lib/locale/__to_ucs.c	(revision 496)
@@ -0,0 +1,27 @@
+/*
+    Locale support implementation through OS/2 Unicode API.
+    Copyright (c) 2003 InnoTek Systemberatung GmbH
+
+    For conditions of distribution and use, see the file COPYING.
+
+    Helper function: convert a MBCS character to a Unicode character.
+    Returns 0 or the length of the MBCS character in bytes.
+*/
+
+#include <uconv.h>
+
+int __to_ucs (UconvObject uconv_obj, const unsigned char *sbcs, size_t len,
+  UniChar *ucs)
+{
+  void *inbuf = (void *)sbcs;
+  UniChar *outbuf = ucs;
+  size_t nonid, in_left = len, out_left = 1;
+
+  int ret = UniUconvToUcs (uconv_obj, &inbuf, &in_left, &outbuf, &out_left,
+    &nonid);
+
+  if ((ret && (ret != ULS_BUFFERFULL)) || nonid || out_left)
+    return 0;
+
+  return len - in_left;
+}
Index: trunk/src/emx/src/lib/locale/isxxx.c
===================================================================
--- trunk/src/emx/src/lib/locale/isxxx.c	(revision 496)
+++ trunk/src/emx/src/lib/locale/isxxx.c	(revision 496)
@@ -0,0 +1,52 @@
+/*
+    Locale support implementation through OS/2 Unicode API.
+    Copyright (c) 2003 InnoTek Systemberatung GmbH
+
+    For conditions of distribution and use, see the file COPYING.
+
+    Usually these routines are not needed (they are inline), but
+    for cases when a program instead of including ctype.h blindly
+    declares them with "extern", they will be linked in.
+*/
+
+#include "libc-alias.h"
+#include <sys/locale.h>
+
+int isalnum (int _c)
+{ return __locale_ctype.cflags [_c & 0xff] & (__UPPER|__LOWER|__DIGIT); }
+
+int isalpha (int _c)
+{ return __locale_ctype.cflags [_c & 0xff] & (__UPPER|__LOWER); }
+
+int iscntrl (int _c)
+{ return __locale_ctype.cflags [_c & 0xff] & (__CNTRL); }
+
+int isdigit (int _c)
+{ return __locale_ctype.cflags [_c & 0xff] & (__DIGIT); }
+
+int isgraph (int _c)
+{ return __locale_ctype.cflags [_c & 0xff] & (__PUNCT|__UPPER|__LOWER|__DIGIT); }
+
+int islower (int _c)
+{ return __locale_ctype.cflags [_c & 0xff] & (__LOWER); }
+
+int isprint (int _c)
+{ return __locale_ctype.cflags [_c & 0xff] & (__PRINT); }
+
+int ispunct (int _c)
+{ return __locale_ctype.cflags [_c & 0xff] & (__PUNCT); }
+
+int isspace (int _c)
+{ return __locale_ctype.cflags [_c & 0xff] & (__SPACE); }
+
+int isupper (int _c)
+{ return __locale_ctype.cflags [_c & 0xff] & (__UPPER); }
+
+int isxdigit (int _c)
+{ return __locale_ctype.cflags [_c & 0xff] & (__XDIGIT); }
+
+int toascii (int _c)
+{ return _c & 0x7f; }
+
+int isascii (int _c)
+{ return (_c & 0x80) != 0; }
Index: trunk/src/emx/src/lib/locale/locale_collate.c
===================================================================
--- trunk/src/emx/src/lib/locale/locale_collate.c	(revision 496)
+++ trunk/src/emx/src/lib/locale/locale_collate.c	(revision 496)
@@ -0,0 +1,27 @@
+/*
+    Locale support implementation through OS/2 Unicode API.
+    Copyright (c) 2003 InnoTek Systemberatung GmbH
+
+    For conditions of distribution and use, see the file COPYING.
+
+    String collation information array.
+*/
+
+#define __INTERNAL_DEFS
+#include <sys/locale.h>
+#include <string.h>
+
+struct __locale_collate __locale_collate;
+
+static void __attribute__((constructor)) __locale_collate_init ()
+{
+  int i;
+
+  memset (&__locale_collate, 0, sizeof (__locale_collate));
+
+  /* There are no prefix MBCS codes */
+  memset (&__locale_collate.mbcsprefix, 0x55, sizeof (__locale_collate.mbcsprefix));
+
+  for (i = 0; i < 256; i++)
+    __locale_collate.weight [i] = i;
+}
Index: trunk/src/emx/src/lib/locale/locale_ctype.c
===================================================================
--- trunk/src/emx/src/lib/locale/locale_ctype.c	(revision 496)
+++ trunk/src/emx/src/lib/locale/locale_ctype.c	(revision 496)
@@ -0,0 +1,85 @@
+/*
+    Locale support implementation through OS/2 Unicode API.
+    Copyright (c) 2003 InnoTek Systemberatung GmbH
+
+    For conditions of distribution and use, see the file COPYING.
+
+    Default character type bits for "C" locale.
+*/
+
+#define __INTERNAL_DEFS
+#include "libc-alias.h"
+#include <sys/locale.h>
+#include <string.h>
+
+
+/*
+    Locale support implementation through OS/2 Unicode API.
+    Copyright (c) 2003 InnoTek Systemberatung GmbH
+
+    For conditions of distribution and use, see the file COPYING.
+
+    Global locale information structure.
+*/
+
+struct __locale_ctype __locale_ctype =
+{
+  cflags:
+  {
+    0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+    0x10, 0x30, 0x30, 0x30, 0x30, 0x30, 0x10, 0x10,
+    0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+    0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+    0xa0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+    0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+    0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+    0x8c, 0x8c, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+    0xc0, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x81,
+    0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+    0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+    0x81, 0x81, 0x81, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+    0xc0, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x82,
+    0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+    0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+    0x82, 0x82, 0x82, 0xc0, 0xc0, 0xc0, 0xc0, 0x10,
+    /* Second half of the table is empty according to ANSI C */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  }
+};
+
+int tolower (int _c)
+{ return __locale_ctype.locase [_c & 0xff]; }
+
+int toupper (int _c)
+{ return __locale_ctype.upcase [_c & 0xff]; }
+
+static void __attribute__((constructor)) __locale_ctype_init ()
+{
+  int i;
+
+  __locale_ctype.uconv = NULL;
+
+  for (i = 0; i < 256; i++)
+  {
+    __locale_ctype.upcase [i] = (i >= 'a' && i <= 'z') ? i & 0xdf : i;
+    __locale_ctype.locase [i] = (i >= 'A' && i <= 'Z') ? i | 0x20 : i;
+  }
+
+  /* There are no prefix MBCS codes */
+  memset (&__locale_ctype.mbcsprefix, 0x55, sizeof (__locale_ctype.mbcsprefix));
+}
Index: trunk/src/emx/src/lib/locale/locale_glob.c
===================================================================
--- trunk/src/emx/src/lib/locale/locale_glob.c	(revision 496)
+++ trunk/src/emx/src/lib/locale/locale_glob.c	(revision 496)
@@ -0,0 +1,31 @@
+/*
+    Locale support implementation through OS/2 Unicode API.
+    Copyright (c) 2003 InnoTek Systemberatung GmbH
+
+    For conditions of distribution and use, see the file COPYING.
+
+    Global locale information structure.
+*/
+
+#define __INTERNAL_DEFS
+#include <sys/locale.h>
+
+const char *__locale_C = "C";
+
+/* This is the global variable containing basic information
+   about current locale. If there is at least one reference to it,
+   the application will also pull in the __locale_init() function
+   which will automatically run at application start time and will
+   initialize this variable to default "C" locale. */
+struct __locale_global __locale =
+{
+  lock: 0
+};
+
+static void __attribute__((constructor)) __locale_init ()
+{
+  int i;
+
+  for (i = LC_ALL; i < __LC_COUNT; i++)
+    __locale.name [i + 1] = (char *)__locale_C;
+}
Index: trunk/src/emx/src/lib/locale/locale_lconv.c
===================================================================
--- trunk/src/emx/src/lib/locale/locale_lconv.c	(revision 496)
+++ trunk/src/emx/src/lib/locale/locale_lconv.c	(revision 496)
@@ -0,0 +1,20 @@
+/*
+    Locale support implementation through OS/2 Unicode API.
+    Copyright (c) 2003 InnoTek Systemberatung GmbH
+
+    For conditions of distribution and use, see the file COPYING.
+
+    Global locale information structure.
+*/
+
+#define __INTERNAL_DEFS
+#include <sys/locale.h>
+#include <limits.h>
+
+struct lconv __locale_lconv =
+{
+  ".", "", "",
+  "", "", "", "", "", "", "-",
+  CHAR_MAX, CHAR_MAX, CHAR_MAX, CHAR_MAX,
+  CHAR_MAX, CHAR_MAX, CHAR_MAX, CHAR_MAX
+};
Index: trunk/src/emx/src/lib/locale/locale_time.c
===================================================================
--- trunk/src/emx/src/lib/locale/locale_time.c	(revision 496)
+++ trunk/src/emx/src/lib/locale/locale_time.c	(revision 496)
@@ -0,0 +1,47 @@
+/*
+    Locale support implementation through OS/2 Unicode API.
+    Copyright (c) 2003 InnoTek Systemberatung GmbH
+
+    For conditions of distribution and use, see the file COPYING.
+
+    Locale-dependent time formatting strings.
+*/
+
+#define __INTERNAL_DEFS
+#include <sys/locale.h>
+#include <string.h>
+
+struct __locale_time __locale_time =
+{
+  date_time_fmt: "%a %b %d %H:%M:%S %Y",
+  date_fmt: "%m/%d/%y",
+  time_fmt: "%H:%M:%S",
+  am: "AM",
+  pm: "PM"
+};
+
+static void init_charptr_array (int count, const char *str, char **array)
+{
+  int idx = 0;
+  while (count--)
+  {
+    array [idx++] = (char *)str;
+    str = strchr (str, 0) + 1;
+  } /* endwhile */
+}
+
+static void __attribute__((constructor)) __locale_time_init ()
+{
+  init_charptr_array (12,
+    "Jan\0Feb\0Mar\0Apr\0May\0Jun\0Jul\0Aug\0Sep\0Oct\0Nov\0Dec",
+    __locale_time.smonths);
+  init_charptr_array (12,
+    "January\0February\0March\0April\0May\0June\0July\0August\0September\0October\0November\0December",
+    __locale_time.lmonths);
+  init_charptr_array (7,
+    "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat",
+    __locale_time.swdays);
+  init_charptr_array (7,
+    "Sunday\0Monday\0Tuesday\0Wednesday\0Thursday\0Friday\0Saturday",
+    __locale_time.lwdays);
+}
Index: trunk/src/emx/src/lib/locale/localeconv.c
===================================================================
--- trunk/src/emx/src/lib/locale/localeconv.c	(revision 496)
+++ trunk/src/emx/src/lib/locale/localeconv.c	(revision 496)
@@ -0,0 +1,18 @@
+/*
+    Locale support implementation through OS/2 Unicode API.
+    Copyright (c) 2003 InnoTek Systemberatung GmbH
+
+    For conditions of distribution and use, see the file COPYING.
+
+    Return the address of the (static) locale information structure.
+*/
+
+#define __INTERNAL_DEFS
+#include "libc-alias.h"
+#include <locale.h>
+#include <sys/locale.h>
+
+struct lconv *_STD(localeconv) (void)
+{
+  return &__locale_lconv;
+}
Index: trunk/src/emx/src/lib/locale/setlocale.c
===================================================================
--- trunk/src/emx/src/lib/locale/setlocale.c	(revision 496)
+++ trunk/src/emx/src/lib/locale/setlocale.c	(revision 496)
@@ -0,0 +1,711 @@
+/*
+    Locale support implementation through OS/2 Unicode API.
+    Copyright (c) 2003 InnoTek Systemberatung GmbH
+
+    For conditions of distribution and use, see the file COPYING.
+
+    Implementation of the setlocale() function.
+*/
+
+#define __INTERNAL_DEFS
+#include "libc-alias.h"
+#include <locale.h>
+#include <sys/locale.h>
+#include <stdlib.h>
+#include <alloca.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <386/builtin.h>
+#include <sys/param.h>
+#include <sys/smutex.h>
+
+#define INCL_DOS
+#include <os2.h>
+#include <unidef.h>
+#include <uconv.h>
+
+/* Instead of strcmp since it's faster */
+#define IS_C_LOCALE(s) (((s) [0] == 'C') && (!(s) [1]))
+
+/* The order of locale categories must equal those in <locale.h> */
+static char *locale_cat =
+  "LC_COLLATE\0LC_CTYPE\0LC_MONETARY\0LC_NUMERIC\0LC_TIME\0LC_MESSAGES\0";
+
+extern void __convert_codepage (const char *cp, UniChar *ucp);
+
+/* Structure used while sorting codepage characters by their weights. */
+struct __collate_weight
+{
+  /* Character code. */
+  unsigned char code;
+  /* Actual weight length. */
+  unsigned char len;
+  /* The weight itself. */
+  UniChar weight [7];
+};
+
+static int convert_ucs (UconvObject uconv_obj, UniChar *in, char **out)
+{
+  size_t usl = UniStrlen (in) + 1;
+  /* Allocate twice as much as we need - just in case every character is DBCS
+     or desired encoding is UCS-2. */
+  size_t osl = usl * 2;
+  size_t in_left = usl;
+  size_t nonid, out_left = osl;
+  char *tmp = malloc (osl);
+  UniChar *inbuf = in;
+  void *outbuf = tmp;
+  int try_count = 0;
+
+try_again:
+
+  if (try_count > 10)
+    /* Well... nobody will say we gave it no chance ... */
+    return -1;
+
+  switch (UniUconvFromUcs (uconv_obj, &inbuf, &in_left, &outbuf, &out_left, &nonid))
+  {
+    case 0:
+      break;
+
+    case UCONV_E2BIG:
+      /* Out buffer too small, make one larger */
+      inbuf = in; in_left = usl;
+      out_left = (osl *= 2);
+      outbuf = tmp = realloc (tmp, osl);
+      try_count++;
+      goto try_again;
+
+    default:
+      /* Unexpected error. */
+      free (tmp);
+      return -1;
+  }
+
+  usl = (char *)outbuf - (char *)tmp;
+  (*out) = (char *)malloc (usl);
+  memcpy (*out, tmp, usl);
+  free (tmp);
+
+  return 0;
+}
+
+static void setname (int category, const char *value, int noalloc)
+{
+  /* LC_ALL is -1. */
+  category++;
+  if (__locale.name [category] && (__locale.name [category] != __locale_C))
+    free (__locale.name [category]);
+  __locale.name [category] = noalloc ? (char *)value : strdup (value);
+}
+
+static int query_array (LocaleObject locale_obj, UconvObject uconv_obj,
+  int count, LocaleItem first, char **out)
+{
+  UniChar *item;
+  int idx;
+
+  for (idx = 0; idx < count; idx++)
+    if (UniQueryLocaleItem (locale_obj, first + idx, &item)
+     || convert_ucs (uconv_obj, item, &out [idx]))
+      return -1;
+
+  return 0;
+}
+
+#define FREE(x) { if (x) free (x); }
+
+static void free_time (struct __locale_time *loc_time)
+{
+  int i;
+
+  for (i = 0; i < 12; i++)
+  {
+    FREE (__locale_time.smonths [i]);
+    FREE (__locale_time.lmonths [i]);
+  }
+
+  for (i = 0; i < 7; i++)
+  {
+    FREE (__locale_time.swdays [i]);
+    FREE (__locale_time.lwdays [i]);
+  }
+
+  FREE (__locale_time.date_time_fmt);
+  FREE (__locale_time.date_fmt);
+  FREE (__locale_time.time_fmt);
+  FREE (__locale_time.am);
+  FREE (__locale_time.pm);
+}
+
+static void free_numeric (struct lconv *lconv)
+{
+  FREE (lconv->decimal_point);
+  FREE (lconv->thousands_sep);
+  FREE (lconv->grouping);
+}
+
+static void free_monetary (struct lconv *lconv)
+{
+  FREE (lconv->int_curr_symbol);
+  FREE (lconv->currency_symbol);
+  FREE (lconv->mon_decimal_point);
+  FREE (lconv->mon_thousands_sep);
+  FREE (lconv->mon_grouping);
+  FREE (lconv->positive_sign);
+  FREE (lconv->negative_sign);
+}
+
+static unsigned char Transform (LocaleObject locale_obj, UconvObject uconv_obj,
+  UniChar (*TransFunc) (LocaleObject, UniChar), UniChar c, unsigned char fallback)
+{
+  unsigned char sbcs;
+  int nb = __from_ucs (uconv_obj, TransFunc (locale_obj, c), &sbcs, 1);
+  return (nb == 1) ? sbcs : fallback;
+}
+
+static int cw_cmp (struct __collate_weight *w1, struct __collate_weight *w2)
+{
+  return UniStrncmp (w1->weight, w2->weight, MIN (w1->len, w2->len));
+}
+
+static void Ucs2Sb (UniChar *ucs, char *sbs, size_t sl)
+{
+  while (sl--)
+    *sbs++ = *ucs++;
+}
+
+static int query_mbcs (UconvObject uconv_obj, char *mbcs,
+  unsigned char *mbcsprefix, int *mb_cur_max)
+{
+  unsigned i;
+  uconv_attribute_t uconv_attr;
+  unsigned char seqlen [256];
+
+  if (UniQueryUconvObject (uconv_obj, &uconv_attr, sizeof (uconv_attr),
+      seqlen, NULL, NULL))
+    return -1;
+
+  if (mb_cur_max)
+    *mb_cur_max = uconv_attr.mb_max_len;
+  *mbcs = (uconv_attr.mb_max_len > 1);
+
+  memset (mbcsprefix, 0, 256/4);
+  for (i = 0; i < 256; i++)
+    if (seqlen [i] != 255)
+      SET_MBCS_PREFIX (mbcsprefix, i, seqlen [i]);
+
+  return 0;
+}
+
+#define ERROR(code) { errno = code; ret = NULL; goto normal_exit; }
+#define ERROR2(code) { _smutex_release (&__locale.lock); ERROR (code); }
+
+char *_STD(setlocale) (int category, const char *locale)
+{
+  LocaleObject locale_obj = 0;
+  UconvObject uconv_obj = 0;
+  UniChar cpbuff [16];
+  struct UniLconv *Lconv;
+  char *x, *l, *m;
+  size_t sl;
+  char *ret = NULL;
+  int i,def_val;
+
+  /* Sanity check. */
+  if (category >= __LC_COUNT)
+  {
+    errno = EINVAL;
+    return NULL;
+  }
+
+  /* Check if user just queries current locale. */
+  if (!locale)
+    return __locale.name [category + 1];
+
+  /* Check if user wants we to do the same job twice. */
+  if (strcmp (locale, __locale.name [category + 1]) == 0)
+    /* We have to return the value of LC_ALL */
+    return __locale.name [0];
+
+  /* We'll use OS/2 Unicode API to query all interesting information.
+     But it works only with Unicode strings, so we have to convert all
+     input information to UCS-2 and all output information from UCS-2. */
+
+  /* Copy locale to a local storage since we'll modify it during parsing.
+     If locale value is a empty string, user wants the defaults fetched from
+     environment. */
+  def_val = (locale [0] == 0);
+  if (def_val)
+  {
+    char *env_val, *cur_cat = locale_cat;
+    int cat;
+
+    if (category == LC_ALL)
+      /* Not in list */
+      cur_cat = "LC_ALL";
+    else
+      for (cat = 0; cat != category; cat++)
+        cur_cat = strchr (cur_cat, 0) + 1;
+
+    env_val = getenv (cur_cat);
+    if (env_val)
+      locale = env_val;
+    else
+    {
+      env_val = getenv ("LANG");
+      if (env_val)
+        locale = env_val;
+      else
+      {
+        /* Not specified nor in environment, ask country info */
+        COUNTRYCODE cc;
+        COUNTRYINFO ci;
+        ULONG il;
+        memset (&cc, 0, sizeof (cc));
+        memset (&ci, 0, sizeof (ci));
+        if (DosQueryCtryInfo (sizeof (ci), &cc, &ci, &il))
+        {
+ctry_error:
+          locale = "C";
+        }
+        else
+        {
+          UniChar cobuff [50];
+          if (UniMapCtryToLocale (ci.country, cobuff, sizeof (cobuff)))
+            goto ctry_error;
+          locale = (char *)cpbuff;
+          Ucs2Sb (cobuff, (char *)locale, UniStrlen (cobuff) + 1);
+        }
+      }
+    }
+  }
+  sl = strlen (locale) + 1;
+  l = (char *)alloca (sl);
+  memcpy (l, locale, sl);
+
+  /* Parse the locale string user passed to us. This is either a string
+     in the XPG format (see below) or a list of values of the
+     form "CATEGORY1=value1;CATEGORY2=value2[;...]", where values are
+     also in XPG format: "language[_territory[.codeset]][@modifier]".
+     Currently we're ignoring the modifier. */
+
+  if (category == LC_ALL)
+  {
+    /* User supplied a list of category=value statements separated with ';'. */
+    if ((m = strchr (l, ';')))
+    {
+      x = l;
+      while (m)
+      {
+        int cat;
+        char *cur_cat = locale_cat;
+
+        /* Remove the ';' at the end of assignment. */
+        *m = 0;
+        for (cat = 0; *cur_cat; cat++, cur_cat = strchr (cur_cat, 0) + 1)
+        {
+          int sl = strlen (cur_cat);
+          if (strncmp (x, cur_cat, sl) == 0
+           && (x [sl] == '='))
+          {
+            setlocale (cat, x + sl + 1);
+            break;
+          }
+        }
+
+        m = strchr (x = m + 1, ';');
+        if (!m && *x)
+          m = strchr (x, 0);
+      }
+    }
+    else
+    {
+      int cat;
+      char *env_val, *cur_cat = locale_cat;
+
+      /* Set all locale categories to given value */
+      for (cat = 0; *cur_cat; cat++, cur_cat = strchr (cur_cat, 0) + 1)
+        /* If user wants default values, check environment first. */
+        if (def_val && (env_val = getenv (cur_cat)))
+          setlocale (cat, env_val);
+        else
+          setlocale (cat, l);
+    }
+  }
+  else
+  {
+    /* Look if the modifier is present and strip it off. */
+    m = strchr (l, '@');
+    if (m) *m = 0;
+
+    /* Look which codepage the user provides. */
+    x = strchr (l, '.');
+    if (x)
+    {
+      *x++ = 0;
+      __convert_codepage (x, cpbuff);
+    }
+    else if (IS_C_LOCALE (l))
+    /* The "C" character encoding maps to ISO8859-1 which is not quite true,
+       but Unicode API doesn't have a codepage that matches the POSIX "C"
+       locale, so that's what we presume when user requests the "C" locale. */
+      memcpy (cpbuff, L"ISO8859-1", 10 * sizeof (wchar_t));
+    else
+    {
+      ULONG cp [3], cplen;
+
+      /* Consider current process codepage as default for specified language */
+      if (DosQueryCp (sizeof (cp), cp, &cplen))
+        ERROR (EINVAL);
+
+      if (UniMapCpToUcsCp (cp [0], cpbuff, sizeof (cpbuff) / sizeof (UniChar)))
+        ERROR (EINVAL);
+    }
+
+    if (UniCreateUconvObject (cpbuff, &uconv_obj))
+      ERROR (EINVAL);
+
+    if (UniCreateLocaleObject (UNI_MBS_STRING_POINTER, l, &locale_obj))
+      ERROR (EINVAL);
+
+    _smutex_request (&__locale.lock);
+
+    switch (category)
+    {
+      case LC_COLLATE:
+      {
+        int j;
+        struct __collate_weight cw [256];
+        if (query_mbcs (uconv_obj, &__locale_collate.mbcs,
+                        __locale_collate.mbcsprefix, NULL))
+          ERROR2 (EINVAL);
+
+        /* Free old locale objects, if any */
+        if (__locale_collate.uconv)
+          UniFreeUconvObject (__locale_collate.uconv);
+        __locale_collate.uconv = NULL;
+        if (__locale_collate.locale)
+          UniFreeLocaleObject (__locale_collate.locale);
+        __locale_collate.locale = NULL;
+
+        if (__locale_collate.mbcs)
+        {
+          /* In MBCS mode we just borrow the conversion and locale objects
+             and leave the real work to the Unicode subsystem. */
+          __locale_collate.locale = locale_obj;
+          locale_obj = NULL;
+          __locale_collate.uconv = uconv_obj;
+          uconv_obj = NULL;
+        }
+        else
+        {
+          /* In SBCS we query the weight of every character and use the
+             weights directly, without the need to invoke the Unicode API. */
+
+          /* Initialize character weights. */
+          for (i = 0; i < 256; i++)
+          {
+            UniChar ucs;
+            UniChar us [2];
+
+            if (!__to_ucs (uconv_obj, (unsigned char *)&i, 1, &ucs))
+              ucs = i;
+
+            us [0] = ucs; us [1] = 0;
+
+            cw [i].code = i;
+            cw [i].len = UniStrxfrm (locale_obj, cw [i].weight, us,
+              sizeof (cw [i].weight) / sizeof (UniChar));
+            if (cw [i].len >= sizeof (cw [i].weight) / sizeof (UniChar))
+              /* This should never happen. */
+              cw [i].len = sizeof (cw [i].weight) / sizeof (UniChar);
+          }
+
+          /* Do bubble sorting since qsort() doesn't guarantee that the order
+             of equal elements stays the same. */
+          for (i = 0; i < 256; i++)
+            for (j = i; j < 256; j++)
+              if (cw_cmp (&cw [j], &cw [j + 1]) > 0)
+                _memswap (&cw [j], &cw [j + 1], sizeof (struct __collate_weight));
+
+          for (i = 0; i < 256; i++)
+            __locale_collate.weight [cw [i].code] = i;
+        }
+
+        break;
+      }
+
+      case LC_CTYPE:
+      {
+        if (query_mbcs (uconv_obj, &__locale_ctype.mbcs,
+                        __locale_ctype.mbcsprefix, &MB_CUR_MAX))
+          ERROR2 (EINVAL);
+
+        /* For speeding up isXXX() and lower/upper case mapping functions
+           we'll cache the type of every character into a variable.
+
+           Do every character separately to avoid errors that could result
+           when some character in the middle cannot be conerted from or to
+           Unicode - this would lead in a shift of the entire string. */
+        for (i = 0; i < 256; i++)
+        {
+          UniChar ucs;
+          unsigned char ct = 0, uc, lc;
+
+          if (!__to_ucs (uconv_obj, (unsigned char *)&i, 1, &ucs))
+            ucs = i;
+
+          /* isxxx() do not support DBCS characters at all */
+          if (!IS_MBCS_PREFIX (__locale_ctype,i))
+          {
+            if (UniQueryUpper (locale_obj, ucs))
+              ct |= __UPPER;
+            if (UniQueryLower (locale_obj, ucs))
+              ct |= __LOWER;
+            if (UniQueryDigit (locale_obj, ucs))
+              ct |= __DIGIT;
+            if (UniQueryXdigit (locale_obj, ucs))
+              ct |= __XDIGIT;
+            if (UniQueryCntrl (locale_obj, ucs))
+              ct |= __CNTRL;
+            if (UniQuerySpace (locale_obj, ucs))
+              ct |= __SPACE;
+            if (UniQueryPunct (locale_obj, ucs))
+              ct |= __PUNCT;
+            if (UniQueryPrint (locale_obj, ucs))
+              ct |= __PRINT;
+
+            uc = Transform (locale_obj, uconv_obj, UniTransUpper, ucs, i);
+            lc = Transform (locale_obj, uconv_obj, UniTransLower, ucs, i);
+          }
+          else
+            uc = lc = i;
+
+          __locale_ctype.cflags [i] = ct;
+
+          __locale_ctype.upcase [i] = uc;
+          __locale_ctype.locase [i] = lc;
+        }
+
+        /* In the "C" locale second half of the cflags table should be empty */
+        if (IS_C_LOCALE (l))
+        {
+          memset (__locale_ctype.cflags + 128, 0, 128);
+          for (i = 128; i < 255; i++)
+          {
+            __locale_ctype.upcase [i] = i;
+            __locale_ctype.locase [i] = i;
+          }
+        }
+
+        if (__locale_ctype.uconv)
+          UniFreeUconvObject (__locale_ctype.uconv);
+        __locale_ctype.uconv = NULL;
+        if (__locale_ctype.locale)
+          UniFreeLocaleObject (__locale_ctype.locale);
+        __locale_ctype.locale = NULL;
+
+        __locale_ctype.uconv = uconv_obj;
+        uconv_obj = NULL;
+
+        if (__locale_ctype.mbcs)
+        {
+          /* In MBCS mode we just borrow the locale object
+             and leave the real work to the Unicode subsystem. */
+          __locale_ctype.locale = locale_obj;
+          locale_obj = NULL;
+        }
+
+        break;
+      }
+
+      case LC_TIME:
+      {
+        struct __locale_time loc_time;
+        UniChar *item;
+
+        memset (&loc_time, 0, sizeof (loc_time));
+
+        if (UniQueryLocaleItem (locale_obj, D_T_FMT, &item)
+         || convert_ucs (uconv_obj, item, &loc_time.date_time_fmt)
+         || UniQueryLocaleItem (locale_obj, D_FMT, &item)
+         || convert_ucs (uconv_obj, item, &loc_time.date_fmt)
+         || UniQueryLocaleItem (locale_obj, T_FMT, &item)
+         || convert_ucs (uconv_obj, item, &loc_time.time_fmt)
+         || UniQueryLocaleItem (locale_obj, AM_STR, &item)
+         || convert_ucs (uconv_obj, item, &loc_time.am)
+         || UniQueryLocaleItem (locale_obj, PM_STR, &item)
+         || convert_ucs (uconv_obj, item, &loc_time.pm)
+         || query_array (locale_obj, uconv_obj, 7, DAY_1, loc_time.lwdays)
+         || query_array (locale_obj, uconv_obj, 7, ABDAY_1, loc_time.swdays)
+         || query_array (locale_obj, uconv_obj, 12, MON_1, loc_time.lmonths)
+         || query_array (locale_obj, uconv_obj, 12, ABMON_1, loc_time.smonths))
+        {
+          free_time (&loc_time);
+          ERROR2 (EINVAL);
+        }
+
+        /* Free the old time formatting info if needed. */
+        if (__locale.name [LC_TIME + 1] != __locale_C)
+          free_time (&__locale_time);
+
+        /* Assign value to static variable. */
+        __locale_time = loc_time;
+        break;
+      }
+
+      case LC_MESSAGES:
+        /* Nothing to do for now */
+        break;
+
+      case LC_NUMERIC:
+      case LC_MONETARY:
+      {
+        struct lconv lconv;
+
+        if (UniQueryLocaleInfo (locale_obj, &Lconv))
+          ERROR2 (EINVAL);
+
+        lconv = __locale_lconv;
+
+#define CONVERT_UCS(field) \
+        if (convert_ucs (uconv_obj, Lconv->field, &lconv.field)) \
+          ERROR3 (EINVAL);
+
+        if (category == LC_NUMERIC)
+        {
+#define ERROR3(code) \
+        { free_numeric (&lconv); UniFreeLocaleInfo (Lconv); ERROR2 (code); }
+
+          /* Initialize fields with NULLs (for the case we will fail) */
+          lconv.decimal_point = lconv.thousands_sep = lconv.grouping = NULL;
+
+          CONVERT_UCS (decimal_point);
+          CONVERT_UCS (thousands_sep);
+          CONVERT_UCS (grouping);
+
+          if (__locale.name [LC_NUMERIC + 1] != __locale_C)
+            free_numeric (&__locale_lconv);
+#undef ERROR3
+        }
+
+        if (category == LC_MONETARY)
+        {
+#define ERROR3(code) { free_monetary (&lconv); ERROR2 (code); }
+
+          /* Initialize fields with NULLs (for the case we will fail) */
+          lconv.int_curr_symbol = lconv.currency_symbol =
+          lconv.mon_decimal_point = lconv.mon_thousands_sep =
+          lconv.mon_grouping = lconv.positive_sign =
+          lconv.negative_sign = NULL;
+
+          CONVERT_UCS (int_curr_symbol);
+          CONVERT_UCS (currency_symbol);
+          CONVERT_UCS (mon_decimal_point);
+          CONVERT_UCS (mon_thousands_sep);
+          CONVERT_UCS (mon_grouping);
+          CONVERT_UCS (positive_sign);
+          CONVERT_UCS (negative_sign);
+          lconv.int_frac_digits = Lconv->int_frac_digits;
+          lconv.frac_digits = Lconv->frac_digits;
+          lconv.p_cs_precedes = Lconv->p_cs_precedes;
+          lconv.p_sep_by_space = Lconv->p_sep_by_space;
+          lconv.n_cs_precedes = Lconv->n_cs_precedes;
+          lconv.n_sep_by_space = Lconv->n_sep_by_space;
+          lconv.p_sign_posn = Lconv->p_sign_posn;
+          lconv.n_sign_posn = Lconv->n_sign_posn;
+
+          if (__locale.name [LC_MONETARY + 1] != __locale_C)
+            free_monetary (&__locale_lconv);
+#undef ERROR3
+        }
+#undef CONVERT_UCS
+
+        UniFreeLocaleInfo (Lconv);
+
+        /* Okay, since we got no errors, copy to its final location. */
+        __locale_lconv = lconv;
+        break;
+      }
+    } /* endcase */
+
+    /* Build and set category value.
+       'l' already contains language and country. */
+    if (IS_C_LOCALE (l))
+      setname (category, l, 0);
+    else
+    {
+      char *loc;
+      int j = 0;
+      i = strlen (l);
+      sl = i + UniStrlen (cpbuff) + 2;
+      loc = malloc (sl);
+      memcpy (loc, l, i);
+      loc [i++] = '.';
+      /* Suppose codepage names always use 7-bit characters (which is true) */
+      while (i < sl)
+        loc [i++] = (char)cpbuff [j++];
+      setname (category, loc, 1);
+    } /* endif */
+
+    _smutex_release (&__locale.lock);
+  }
+
+  /* Check if all locale categories are equal. If not, return a list in
+     the "category=value" format, otherwise just return the value itself. */
+  ret = __locale.name [1];
+  sl = 0;
+  x = locale_cat;
+  for (i = 0; i < __LC_COUNT; i++)
+  {
+    if (ret && strcmp (ret, __locale.name [i + 1]) != 0)
+      /* We have to return a list value. */
+      ret = NULL;
+    sl += strlen (x) + strlen (__locale.name [i + 1]) + 2;
+    x = strchr (x, 0) + 1;
+  }
+
+  i = 0;
+  if (!ret)
+  {
+    /* Generate a list value. */
+    ret = malloc (sl);
+    x = locale_cat;
+    sl = 0;
+    for (/*i = 0*/; i < __LC_COUNT; i++)
+    {
+      strcpy (ret + sl, x);
+      sl += strlen (ret + sl);
+      ret [sl++] = '=';
+      strcpy (ret + sl, __locale.name [i + 1]);
+      sl += strlen (ret + sl);
+      if (i < __LC_COUNT - 1)
+        ret [sl++] = ';';
+      x = strchr (x, 0) + 1;
+    }
+  } /* endif */
+
+  /* If all values (except LC_ALL) are equal, ret is set to that value.
+     In this case we must set LC_ALL to that value as well.
+     If values are partially different, ret points to a composite value.
+     In this case we also must set LC_ALL to this composite value.
+     If i is not zero, then we have a composite value which is already
+     allocated from heap (so avoid setname() allocating it again). */
+  setname (LC_ALL, ret, i);
+
+  /* But in any case, the returned value is the one actually set for
+     the category in question. */
+  ret = __locale.name [category + 1];
+
+normal_exit:
+  if (locale_obj)
+    UniFreeLocaleObject (locale_obj);
+  if (uconv_obj)
+    UniFreeUconvObject (uconv_obj);
+
+  return ret;
+}
+
+#undef ERROR
