[haiku-commits] r36017 - in haiku/branches/developer/zooey/posix-locale: headers/posix src/system/libroot/posix/locale src/tests/system/libroot/posix

  • From: zooey@xxxxxxxxxxxxxxx
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Wed, 31 Mar 2010 23:23:07 +0200 (CEST)

Author: zooey
Date: 2010-03-31 23:23:07 +0200 (Wed, 31 Mar 2010)
New Revision: 36017
Changeset: http://dev.haiku-os.org/changeset/36017/haiku

Added:
   
haiku/branches/developer/zooey/posix-locale/src/tests/system/libroot/posix/locale_test.cpp
Modified:
   haiku/branches/developer/zooey/posix-locale/headers/posix/locale.h
   
haiku/branches/developer/zooey/posix-locale/src/system/libroot/posix/locale/ICULocaleBackend.cpp
   
haiku/branches/developer/zooey/posix-locale/src/system/libroot/posix/locale/ICULocaleBackend.h
   
haiku/branches/developer/zooey/posix-locale/src/system/libroot/posix/locale/Jamfile
   
haiku/branches/developer/zooey/posix-locale/src/system/libroot/posix/locale/setlocale.cpp
   
haiku/branches/developer/zooey/posix-locale/src/tests/system/libroot/posix/Jamfile
Log:
* adjusted setlocale() to support setting a different locale for each category
* fleshed out backend some more, now there are (more or less stubbed) classes
  for each category, which will hold the respective data once we get to it
* added LC_LAST and commentary note to locale.h
* added trivial test locale_test.cpp
basic setting and getting of different locales seems to work now (still no 
effect, though)


Modified: haiku/branches/developer/zooey/posix-locale/headers/posix/locale.h
===================================================================
--- haiku/branches/developer/zooey/posix-locale/headers/posix/locale.h  
2010-03-31 18:34:03 UTC (rev 36016)
+++ haiku/branches/developer/zooey/posix-locale/headers/posix/locale.h  
2010-03-31 21:23:07 UTC (rev 36017)
@@ -1,6 +1,6 @@
 #ifndef _LOCALE_H_
 #define _LOCALE_H_
-/* 
+/*
 ** Distributed under the terms of the OpenBeOS License.
 */
 
@@ -34,6 +34,11 @@
 #define        LC_NUMERIC      4
 #define        LC_TIME         5
 #define        LC_MESSAGES     6
+/*
+ * the values above must be kept in loopable order (i.e. strictly increasing
+ * with no holes) and in sync with the value below
+ */
+#define        LC_LAST         LC_MESSAGES
 
 #ifdef __cplusplus
 extern "C" {

Modified: 
haiku/branches/developer/zooey/posix-locale/src/system/libroot/posix/locale/ICULocaleBackend.cpp
===================================================================
--- 
haiku/branches/developer/zooey/posix-locale/src/system/libroot/posix/locale/ICULocaleBackend.cpp
    2010-03-31 18:34:03 UTC (rev 36016)
+++ 
haiku/branches/developer/zooey/posix-locale/src/system/libroot/posix/locale/ICULocaleBackend.cpp
    2010-03-31 21:23:07 UTC (rev 36017)
@@ -10,6 +10,7 @@
 
 #include <limits.h>
 #include <locale.h>
+#include <string.h>
 
 
 namespace BPrivate {
@@ -43,6 +44,129 @@
 };
 
 
+CategoryData::CategoryData()
+{
+       *fPosixLocaleName = '\0';
+       *fGivenCharset = '\0';
+}
+
+
+CategoryData::~CategoryData()
+{
+}
+
+
+status_t
+CategoryData::SetTo(const Locale& locale, const char* posixLocaleName)
+{
+       if (!posixLocaleName)
+               return B_BAD_VALUE;
+
+       fLocale = locale;
+       strlcpy(fPosixLocaleName, posixLocaleName, skMaxPosixLocaleNameLen);
+       *fGivenCharset = '\0';
+
+       // POSIX locales often contain an embedded charset, but ICU does not
+       // handle these within locales (that part of the name is simply
+       // ignored).
+       // We need to fetch the charset specification and lookup an appropriate
+       // ICU charset converter. This converter will later be used to get info
+       // about ctype properties.
+       const char* charsetStart = strchr(fPosixLocaleName, '.');
+       if (charsetStart) {
+               ++charsetStart;
+               int l = 0;
+               while (charsetStart[l] != '\0' && charsetStart[l] != '@')
+                       ++l;
+               snprintf(fGivenCharset, UCNV_MAX_CONVERTER_NAME_LENGTH, "%.*s", 
l,
+                       charsetStart);
+       }
+
+       return B_OK;
+}
+
+
+status_t
+CollateData::SetTo(const Locale& locale, const char* posixLocaleName)
+{
+       status_t result = CategoryData::SetTo(locale, posixLocaleName);
+
+       return result;
+}
+
+
+CtypeData::CtypeData()
+       :
+       fConverter(NULL)
+{
+}
+
+
+CtypeData::~CtypeData()
+{
+       if (fConverter)
+               ucnv_close(fConverter);
+}
+
+
+status_t
+CtypeData::SetTo(const Locale& locale, const char* posixLocaleName)
+{
+       status_t result = CategoryData::SetTo(locale, posixLocaleName);
+       if (result == B_OK && *fGivenCharset != '\0') {
+               UErrorCode icuStatus = U_ZERO_ERROR;
+               fConverter = ucnv_open(fGivenCharset, &icuStatus);
+               if (fConverter == NULL)
+                       result = B_BAD_VALUE;
+       }
+
+       return result;
+}
+
+
+status_t
+MessagesData::SetTo(const Locale& locale, const char* posixLocaleName)
+{
+       status_t result = CategoryData::SetTo(locale, posixLocaleName);
+
+       return result;
+}
+
+
+status_t
+MonetaryData::SetTo(const Locale& locale, const char* posixLocaleName)
+{
+       status_t result = CategoryData::SetTo(locale, posixLocaleName);
+
+       return result;
+}
+
+
+status_t
+NumericData::SetTo(const Locale& locale, const char* posixLocaleName)
+{
+       status_t result = CategoryData::SetTo(locale, posixLocaleName);
+
+       return result;
+}
+
+
+status_t
+TimeData::SetTo(const Locale& locale, const char* posixLocaleName)
+{
+       status_t result = CategoryData::SetTo(locale, posixLocaleName);
+
+       return result;
+}
+
+
+extern "C" LocaleBackend*
+CreateInstance()
+{
+       return new(std::nothrow) ICULocaleBackend();
+}
+
+
 ICULocaleBackend::ICULocaleBackend()
 {
 }
@@ -54,9 +178,52 @@
 
 
 const char*
-ICULocaleBackend::SetLocale(int category, const char* locale)
+ICULocaleBackend::SetLocale(int category, const char* posixLocaleName)
 {
-       return locale;
+       if (posixLocaleName == NULL)
+               return _QueryLocale(category);
+
+       Locale locale = Locale::createCanonical(posixLocaleName);
+       switch (category) {
+               case LC_ALL:
+                       if (fCollateData.SetTo(locale, posixLocaleName) != B_OK
+                               || fCtypeData.SetTo(locale, posixLocaleName) != 
B_OK
+                               || fMessagesData.SetTo(locale, posixLocaleName) 
!= B_OK
+                               || fMonetaryData.SetTo(locale, posixLocaleName) 
!= B_OK
+                               || fNumericData.SetTo(locale, posixLocaleName) 
!= B_OK
+                               || fTimeData.SetTo(locale, posixLocaleName) != 
B_OK)
+                               return NULL;
+                       break;
+               case LC_COLLATE:
+                       if (fCollateData.SetTo(locale, posixLocaleName) != B_OK)
+                               return NULL;
+                       break;
+               case LC_CTYPE:
+                       if (fCtypeData.SetTo(locale, posixLocaleName) != B_OK)
+                               return NULL;
+                       break;
+               case LC_MESSAGES:
+                       if (fMessagesData.SetTo(locale, posixLocaleName) != 
B_OK)
+                               return NULL;
+                       break;
+               case LC_MONETARY:
+                       if (fMonetaryData.SetTo(locale, posixLocaleName) != 
B_OK)
+                               return NULL;
+                       break;
+               case LC_NUMERIC:
+                       if (fNumericData.SetTo(locale, posixLocaleName) != B_OK)
+                               return NULL;
+                       break;
+               case LC_TIME:
+                       if (fTimeData.SetTo(locale, posixLocaleName) != B_OK)
+                               return NULL;
+                       break;
+               default:
+                       // unsupported category
+                       return NULL;
+       }
+
+       return posixLocaleName;
 }
 
 
@@ -67,10 +234,48 @@
 }
 
 
-extern "C" LocaleBackend*
-CreateInstance()
+const char*
+ICULocaleBackend::_QueryLocale(int category)
 {
-       return new(std::nothrow) ICULocaleBackend();
+       switch (category) {
+               case LC_ALL:
+               {
+                       // if all categories have the same locale, return that
+                       const char* locale = fCollateData.PosixLocaleName();
+                       if (strcmp(locale, fCtypeData.PosixLocaleName()) == 0
+                               && strcmp(locale, 
fMessagesData.PosixLocaleName()) == 0
+                               && strcmp(locale, 
fMonetaryData.PosixLocaleName()) == 0
+                               && strcmp(locale, 
fNumericData.PosixLocaleName()) == 0
+                               && strcmp(locale, fTimeData.PosixLocaleName()) 
== 0)
+                               return locale;
+
+                       // build a string with all info (at least glibc does 
it, too)
+                       static char localeDescr[512];
+                       snprintf(localeDescr, sizeof(localeDescr),
+                               
"LC_COLLATE=%s;LC_CTYPE=%s;LC_MESSAGES=%s;LC_MONETARY=%s;"
+                               "LC_NUMERIC=%s;LC_TIME=%s",
+                               fCollateData.PosixLocaleName(), 
fCtypeData.PosixLocaleName(),
+                               fMessagesData.PosixLocaleName(),
+                               fMonetaryData.PosixLocaleName(), 
fNumericData.PosixLocaleName(),
+                               fTimeData.PosixLocaleName());
+                       return localeDescr;
+               }
+               case LC_COLLATE:
+                       return fCollateData.PosixLocaleName();
+               case LC_CTYPE:
+                       return fCtypeData.PosixLocaleName();
+               case LC_MESSAGES:
+                       return fMessagesData.PosixLocaleName();
+               case LC_MONETARY:
+                       return fMonetaryData.PosixLocaleName();
+               case LC_NUMERIC:
+                       return fNumericData.PosixLocaleName();
+               case LC_TIME:
+                       return fTimeData.PosixLocaleName();
+               default:
+                       // unsupported category
+                       return NULL;
+       }
 }
 
 

Modified: 
haiku/branches/developer/zooey/posix-locale/src/system/libroot/posix/locale/ICULocaleBackend.h
===================================================================
--- 
haiku/branches/developer/zooey/posix-locale/src/system/libroot/posix/locale/ICULocaleBackend.h
      2010-03-31 18:34:03 UTC (rev 36016)
+++ 
haiku/branches/developer/zooey/posix-locale/src/system/libroot/posix/locale/ICULocaleBackend.h
      2010-03-31 21:23:07 UTC (rev 36017)
@@ -6,19 +6,100 @@
 #define _ICU_LOCALE_BACKEND_H
 
 
+#include <unicode/locid.h>
+#include <unicode/ucnv.h>
+
 #include "LocaleBackend.h"
 
 
 namespace BPrivate {
 
 
+class CategoryData {
+public:
+                                                               CategoryData();
+virtual                                                ~CategoryData();
+
+virtual                status_t                        SetTo(const Locale& 
locale,
+                                                                       const 
char* posixLocaleName);
+
+                       const char *            PosixLocaleName()
+                                                                       { 
return fPosixLocaleName; }
+
+protected:
+static const   uint16                  skMaxPosixLocaleNameLen = 128;
+                       Locale                          fLocale;
+                       char                            
fPosixLocaleName[skMaxPosixLocaleNameLen];
+                       char                            
fGivenCharset[UCNV_MAX_CONVERTER_NAME_LENGTH];
+};
+
+
+class CollateData : public CategoryData {
+public:
+virtual                status_t                        SetTo(const Locale& 
locale,
+                                                                       const 
char* posixLocaleName);
+};
+
+
+class CtypeData : public CategoryData {
+public:
+                                                               CtypeData();
+virtual                                                ~CtypeData();
+
+virtual                status_t                        SetTo(const Locale& 
locale,
+                                                                       const 
char* posixLocaleName);
+
+private:
+                       UConverter*                     fConverter;
+};
+
+
+class MessagesData : public CategoryData {
+public:
+virtual                status_t                        SetTo(const Locale& 
locale,
+                                                                       const 
char* posixLocaleName);
+};
+
+
+class MonetaryData : public CategoryData {
+public:
+virtual                status_t                        SetTo(const Locale& 
locale,
+                                                                       const 
char* posixLocaleName);
+};
+
+
+class NumericData : public CategoryData {
+public:
+virtual                status_t                        SetTo(const Locale& 
locale,
+                                                                       const 
char* posixLocaleName);
+};
+
+
+class TimeData : public CategoryData {
+public:
+virtual                status_t                        SetTo(const Locale& 
locale,
+                                                                       const 
char* posixLocaleName);
+};
+
+
 class ICULocaleBackend : public LocaleBackend {
 public:
                                                                
ICULocaleBackend();
 virtual                                                        
~ICULocaleBackend();
 
-virtual                const char*                     SetLocale(int category, 
const char* locale);
+virtual                const char*                     SetLocale(int category,
+                                                                       const 
char* posixLocaleName);
 virtual                struct lconv*           LocaleConv();
+
+private:
+                       const char*                     _QueryLocale(int 
category);
+
+                       CollateData                     fCollateData;
+                       CtypeData                       fCtypeData;
+                       MessagesData            fMessagesData;
+                       MonetaryData            fMonetaryData;
+                       NumericData                     fNumericData;
+                       TimeData                        fTimeData;
 };
 
 

Modified: 
haiku/branches/developer/zooey/posix-locale/src/system/libroot/posix/locale/Jamfile
===================================================================
--- 
haiku/branches/developer/zooey/posix-locale/src/system/libroot/posix/locale/Jamfile
 2010-03-31 18:34:03 UTC (rev 36016)
+++ 
haiku/branches/developer/zooey/posix-locale/src/system/libroot/posix/locale/Jamfile
 2010-03-31 21:23:07 UTC (rev 36017)
@@ -12,7 +12,11 @@
        #wcrtomb.c
 ;
 
+UseLibraryHeaders icu ;
+
 SharedLibrary libroot-addon-locale.so
        :
        ICULocaleBackend.cpp
+       :
+       $(TARGET_LIBSTDC++) libicu-common.so libicu-i18n.so
 ;

Modified: 
haiku/branches/developer/zooey/posix-locale/src/system/libroot/posix/locale/setlocale.cpp
===================================================================
--- 
haiku/branches/developer/zooey/posix-locale/src/system/libroot/posix/locale/setlocale.cpp
   2010-03-31 18:34:03 UTC (rev 36016)
+++ 
haiku/branches/developer/zooey/posix-locale/src/system/libroot/posix/locale/setlocale.cpp
   2010-03-31 21:23:07 UTC (rev 36017)
@@ -15,60 +15,105 @@
 using BPrivate::LocaleBackend;
 
 
-static const char*
-LocaleAccordingToEnvironment(int category)
+static status_t
+GetLocalesFromEnvironment(int category, const char** locales)
 {
        const char* locale = getenv("LC_ALL");
-       if (!locale || *locale == '\0') {
-               switch (category) {
-                       case LC_COLLATE:
-                               locale = getenv("LC_COLLATE");
-                               break;
-                       case LC_CTYPE:
-                               locale = getenv("LC_CTYPE");
-                               break;
-                       case LC_MESSAGES:
-                               locale = getenv("LC_MESSAGES");
-                               break;
-                       case LC_MONETARY:
-                               locale = getenv("LC_MONETARY");
-                               break;
-                       case LC_NUMERIC:
-                               locale = getenv("LC_NUMERIC");
-                               break;
-                       case LC_TIME:
-                               locale = getenv("LC_TIME");
-                               break;
+       if (locale && *locale)
+               locales[category] = locale;
+       else {
+               // the order of the names must match the one specified in 
locale.h
+               const char* categoryNames[] = {
+                       "LC_ALL",
+                       "LC_COLLATE",
+                       "LC_CTYPE",
+                       "LC_MONETARY",
+                       "LC_NUMERIC",
+                       "LC_TIME",
+                       "LC_MESSAGES"
+               };
+               int from, to;
+               if (category == LC_ALL) {
+                       // we need to check each real category if all of them 
should be set
+                       from = 1;
+                       to = LC_LAST;
+               } else
+                       from = to = category;
+               bool haveDifferentLocales = false;
+               locale = NULL;
+               for (int lc = from; lc <= to; lc++) {
+                       const char* lastLocale = locale;
+                       locale = getenv(categoryNames[lc]);
+                       if (!locale || *locale == '\0')
+                               locale = getenv("LANG");
+                       if (!locale || *locale == '\0')
+                               locale = "POSIX";
+                       locales[lc] = locale;
+                       if (lastLocale && strcmp(locale, lastLocale) != 0)
+                               haveDifferentLocales = true;
                }
-               if (!locale || *locale == '\0')
-                       locale = getenv("LANG");
-               if (!locale || *locale == '\0')
-                       locale = "POSIX";
+               if (!haveDifferentLocales) {
+                       // we can set all locales at once
+                       locales[LC_ALL] = locale;
+               }
        }
-       return locale;
+
+       return B_OK;
 }
 
 
 extern "C" char*
 setlocale(int category, const char* locale)
 {
-       // update locale according to environment vars, if requested
-       if (locale && *locale == '\0')
-               locale = LocaleAccordingToEnvironment(category);
+       if (category < 0 || category > LC_LAST)
+               return NULL;
 
-       // for any locale other than POSIX/C, we try to activate the ICU 
backend,
-       // unless the backend has already been loaded, or a query has been 
requested
-       if (!gLocaleBackend && locale != NULL && strcmp(locale, "POSIX") != 0
-                       && strcmp(locale, "C") != 0) {
-               if (LocaleBackend::LoadBackend() != B_OK)
-                               return NULL;
+       if (locale == NULL) {
+               // query the locale of the given category
+               if (gLocaleBackend != NULL)
+                       return 
const_cast<char*>(gLocaleBackend->SetLocale(category, NULL));
+               else
+                       return "POSIX";
        }
 
-       if (gLocaleBackend)
-               return const_cast<char*>(gLocaleBackend->SetLocale(category, 
locale));
+       // we may have to invoke SetLocale once for each category, so we use an
+       // array to collect the locale per category
+       const char* locales[LC_LAST + 1];
+       for (int lc = 0; lc <= LC_LAST; lc++)
+               locales[lc] = NULL;
 
-       if (locale && strcmp(locale, "C") == 0)
-               return "C";
+       if (*locale == '\0')
+               GetLocalesFromEnvironment(category, locales);
+       else
+               locales[category] = locale;
 
+       if (!gLocaleBackend) {
+               // for any locale other than POSIX/C, we try to activate the ICU
+               // backend
+               bool needBackend = false;
+               for (int lc = 0; lc <= LC_LAST; lc++) {
+                       if (locales[lc] != NULL && strcmp(locales[lc], "POSIX") 
!= 0
+                                       && strcmp(locales[lc], "C") != 0) {
+                               needBackend = true;
+                               break;
+                       }
+               }
+               if (needBackend && LocaleBackend::LoadBackend() != B_OK)
+                       return NULL;
+       }
+
+       if (gLocaleBackend != NULL) {
+               for (int lc = 0; lc <= LC_LAST; lc++) {
+                       if (locales[lc] != NULL) {
+                               locale = gLocaleBackend->SetLocale(lc, 
locales[lc]);
+                               if (lc == LC_ALL) {
+                                       // skip the rest, LC_ALL overrides
+                                       return const_cast<char*>(locale);
+                               }
+                       }
+               }
+               return const_cast<char*>(gLocaleBackend->SetLocale(category, 
NULL));
+       }
+
        return "POSIX";
 }

Modified: 
haiku/branches/developer/zooey/posix-locale/src/tests/system/libroot/posix/Jamfile
===================================================================
--- 
haiku/branches/developer/zooey/posix-locale/src/tests/system/libroot/posix/Jamfile
  2010-03-31 18:34:03 UTC (rev 36016)
+++ 
haiku/branches/developer/zooey/posix-locale/src/tests/system/libroot/posix/Jamfile
  2010-03-31 21:23:07 UTC (rev 36017)
@@ -76,6 +76,10 @@
        : xsi_sem_test1.cpp
 ;
 
+SimpleTest locale_test
+       : locale_test.cpp
+;
+
 SimpleTest mbtest
        : mbtest.c
 ;

Added: 
haiku/branches/developer/zooey/posix-locale/src/tests/system/libroot/posix/locale_test.cpp
===================================================================
--- 
haiku/branches/developer/zooey/posix-locale/src/tests/system/libroot/posix/locale_test.cpp
                          (rev 0)
+++ 
haiku/branches/developer/zooey/posix-locale/src/tests/system/libroot/posix/locale_test.cpp
  2010-03-31 21:23:07 UTC (rev 36017)
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2010, Oliver Tappe, zooey@xxxxxxxxxxxxxxx
+ * Distributed under the terms of the MIT License.
+ */
+
+
+#include <locale.h>
+#include <stdio.h>
+
+
+/*
+ * Test several different aspects of the POSIX locale and the functions
+ * influenced by it.
+ */
+int
+main(void)
+{
+       const char* locales[] = {
+               "POSIX",
+               "C",
+               "de_DE",
+               "de_DE.iso8859-1",
+               "De_dE.IsO8859-15",
+               "de_DE.utf8",
+               "de_DE.UTF-8",
+               "de_DE@euro",
+               "de_DE@EURO",
+               "de_DE.utf-8@Euro",
+               "en_US.ANSI_X3.4-1968",
+               "hr_HR.ISO-8859-2",
+               "POSIX",
+               "C",
+               NULL
+       };
+       for(int i = 0; locales[i] != NULL; ++i) {
+               printf("locale '%s': ", locales[i]);
+               char* result = setlocale(LC_ALL, locales[i]);
+               if (!result) {
+                       printf("not ok\n");
+                       continue;
+               }
+               printf("ok (%s)\n", result);
+       }
+
+       return 0;
+}


Other related posts:

  • » [haiku-commits] r36017 - in haiku/branches/developer/zooey/posix-locale: headers/posix src/system/libroot/posix/locale src/tests/system/libroot/posix - zooey