[rkward-cvs] SF.net SVN: rkward: [1558] trunk/rkward/rkward/rbackend

tfry at users.sourceforge.net tfry at users.sourceforge.net
Sun Mar 11 17:28:13 UTC 2007


Revision: 1558
          http://svn.sourceforge.net/rkward/?rev=1558&view=rev
Author:   tfry
Date:     2007-03-11 10:28:13 -0700 (Sun, 11 Mar 2007)

Log Message:
-----------
Initial support for switching locales (let's hope it generally isn't needed, though)

Modified Paths:
--------------
    trunk/rkward/rkward/rbackend/Makefile.am
    trunk/rkward/rkward/rbackend/rembedinternal.cpp
    trunk/rkward/rkward/rbackend/rembedinternal.h
    trunk/rkward/rkward/rbackend/rinterface.cpp
    trunk/rkward/rkward/rbackend/rpackages/rkward/R/internal.R

Added Paths:
-----------
    trunk/rkward/rkward/rbackend/rklocalesupport.cpp
    trunk/rkward/rkward/rbackend/rklocalesupport.h

Modified: trunk/rkward/rkward/rbackend/Makefile.am
===================================================================
--- trunk/rkward/rkward/rbackend/Makefile.am	2007-03-11 13:51:43 UTC (rev 1557)
+++ trunk/rkward/rkward/rbackend/Makefile.am	2007-03-11 17:28:13 UTC (rev 1558)
@@ -5,7 +5,7 @@
 
 noinst_LIBRARIES =  librbackend.a
 librbackend_a_SOURCES = rembedinternal.cpp rinterface.cpp rthread.cpp rcommand.cpp rcommandreceiver.cpp rcommandstack.cpp \
-rdata.cpp rkpthreadsupport.cpp
+rdata.cpp rkpthreadsupport.cpp rklocalesupport.cpp
 noinst_HEADERS = rembedinternal.h rinterface.h rthread.h rcommand.h rcommandreceiver.h rcommandstack.h \
-rdata.h rkpthreadsupport.h
+rdata.h rkpthreadsupport.h rklocalesupport.h
 SUBDIRS =  rpackages

Modified: trunk/rkward/rkward/rbackend/rembedinternal.cpp
===================================================================
--- trunk/rkward/rkward/rbackend/rembedinternal.cpp	2007-03-11 13:51:43 UTC (rev 1557)
+++ trunk/rkward/rkward/rbackend/rembedinternal.cpp	2007-03-11 17:28:13 UTC (rev 1558)
@@ -24,12 +24,24 @@
 #define TRUE (const bool)0
 #define FALSE (const bool)!0
 #include <qstring.h>
+#include <qtextcodec.h>
+#include "../debug.h"
 #undef TRUE
 #undef FALSE
 
+#include "rklocalesupport.h"
+
 extern "C" {
 #define R_INTERFACE_PTRS 1
 
+// needed to detect CHARSXP encoding
+#define USE_RINTERNALS 1
+#define UTF8_MASK (1<<3)
+#define IS_UTF8(x) ((x)->sxpinfo.gp & UTF8_MASK)
+#define LATIN1_MASK (1<<2)
+#define IS_LATIN1(x) ((x)->sxpinfo.gp & LATIN1_MASK)
+// end
+
 #include "Rdefines.h"
 #include "R_ext/Rdynload.h"
 #include "R_ext/eventloop.h"
@@ -104,6 +116,8 @@
 
 // ############## R Standard callback overrides BEGIN ####################
 void RSuicide (char* message) {
+	RK_TRACE (RBACKEND);
+
 	RCallbackArgs args;
 	args.type = RCallbackArgs::RSuicide;
 	args.chars_a = &message;
@@ -112,6 +126,8 @@
 }
 
 void RShowMessage (char* message) {
+	RK_TRACE (RBACKEND);
+
 	RCallbackArgs args;
 	args.type = RCallbackArgs::RShowMessage;
 	args.chars_a = &message;
@@ -119,6 +135,8 @@
 }
 
 int RReadConsole (char* prompt, unsigned char* buf, int buflen, int hist) {
+	RK_TRACE (RBACKEND);
+
 #ifdef USE_R_REPLDLLDO1
 	// handle requests for new code
 	if (repldlldo1_wants_code) {
@@ -163,19 +181,25 @@
 }
 
 void RWriteConsole (char *buf, int buflen) {
+	RK_TRACE (RBACKEND);
+
 /*	RCallbackArgs args;
 	args.type = RCallbackArgs::RWriteConsole;
 	args.chars_a = &buf;
 	args.int_a = buflen;
 	REmbedInternal::this_pointer->handleStandardCallback (&args); */
-	REmbedInternal::this_pointer->handleOutput (QString::fromLocal8Bit (buf, buflen), buflen, true);
+	REmbedInternal::this_pointer->handleOutput (REmbedInternal::this_pointer->current_locale_codec->toUnicode (buf, buflen), buflen, true);
 }
 
 void RResetConsole () {
+	RK_TRACE (RBACKEND);
+
 // we leave this un-implemented on purpose! We simply don't want that sort of thing to be done.
 }
 
 void RFlushConsole () {
+	RK_TRACE (RBACKEND);
+
 /* nope, we're not going to do the line below after all. Two reasons:
 1) We'd still have to add mutex protection around this call (ok, doable of course)
 2) I don't think we need it at all: We do our own flushing, and R rarely flushes, for obscure reasons, anyway.
@@ -184,10 +208,14 @@
 }
 
 void RClearerrConsole () {
+	RK_TRACE (RBACKEND);
+
 // we leave this un-implemented on purpose! We simply don't want that sort of thing to be done.
 }
 
 void RCleanUp (SA_TYPE saveact, int status, int RunLast) {
+	RK_TRACE (RBACKEND);
+
 	if (saveact != SA_SUICIDE) {
 		RCallbackArgs args;
 		args.type = RCallbackArgs::RCleanUp;
@@ -211,6 +239,8 @@
 }
 
 int RShowFiles (int nfile, char **file, char **headers, char *wtitle, Rboolean del, char *pager) {
+	RK_TRACE (RBACKEND);
+
 	RCallbackArgs args;
 	args.type = RCallbackArgs::RShowFiles;
 	args.int_a = nfile;
@@ -227,6 +257,8 @@
 }
 
 int RChooseFile (int isnew, char *buf, int len) {
+	RK_TRACE (RBACKEND);
+
 	RCallbackArgs args;
 	args.type = RCallbackArgs::RChooseFile;
 	args.int_a = isnew;
@@ -240,6 +272,8 @@
 }
 
 int REditFiles (int nfile, char **file, char **title, char *editor) {
+	RK_TRACE (RBACKEND);
+
 	RCallbackArgs args;
 	args.type = RCallbackArgs::REditFiles;
 	args.int_a = nfile;
@@ -254,6 +288,8 @@
 }
 
 int REditFile (char *buf) {
+	RK_TRACE (RBACKEND);
+
 	char *editor = "none";
 	char *title = "";
 
@@ -263,6 +299,8 @@
 
 #ifdef USE_R_REPLDLLDO1
 void RBusy (int busy) {
+	RK_TRACE (RBACKEND);
+
 	// R_ReplDLLDo1 calls R_Busy (1) after reading in code (if needed), parsing it, and right before evaluating it.
 	if (busy) {
 		repldlldo1_wants_code = false;
@@ -276,10 +314,15 @@
 char *REmbedInternal::na_char_internal = new char;
 
 REmbedInternal::REmbedInternal () {
+	RK_TRACE (RBACKEND);
+
+	current_locale_codec = QTextCodec::codecForLocale ();
 	r_running = false;
 }
 
 void REmbedInternal::connectCallbacks () {
+	RK_TRACE (RBACKEND);
+
 // R standard callback pointers.
 // Rinterface.h thinks this can only ever be done on aqua, apparently. Here, we define it the other way around, i.e. #ifndef instead of #ifdef
 // No, does not work -> undefined reference! -> TODO: nag R-devels
@@ -312,9 +355,12 @@
 }
 
 REmbedInternal::~REmbedInternal () {
+	RK_TRACE (RBACKEND);
 }
 
 void REmbedInternal::shutdown (bool suicidal) {
+	RK_TRACE (RBACKEND);
+
 	if (!REmbedInternal::this_pointer->r_running) return;		// already shut down
 
 // Code-recipe below essentially copied from http://stat.ethz.ch/R-manual/R-devel/doc/manual/R-exts.html#Linking-GUIs-and-other-front_ends-to-R
@@ -373,6 +419,8 @@
 
 /** This function is the R side wrapper around stringsToStringList */
 QString *SEXPToStringList (SEXP from_exp, unsigned int *count) {
+	RK_TRACE (RBACKEND);
+
 	// bad format? coerce the vector first
 	if (TYPEOF (from_exp) != STRSXP) {
 		SEXP strexp;
@@ -390,12 +438,18 @@
 		SEXP dummy = VECTOR_ELT (from_exp, i);
 
 		if (TYPEOF (dummy) != CHARSXP) {
-			list[i] = QString::fromLocal8Bit ("not defined");	// can this ever happen?
+			list[i] = QString ("not defined");	// can this ever happen?
 		} else {
 			if (dummy == NA_STRING) {
 				list[i] = QString::null;
 			} else {
-				list[i] = QString::fromLocal8Bit ((char *) STRING_PTR (dummy));
+				if (IS_UTF8 (dummy)) {
+					list[i] = QString::fromUtf8 ((char *) STRING_PTR (dummy));
+				} else if (IS_LATIN1 (dummy)) {
+					list[i] = QString::fromLatin1 ((char *) STRING_PTR (dummy));
+				} else {
+					list[i] = REmbedInternal::this_pointer->current_locale_codec->toUnicode ((char *) STRING_PTR (dummy));
+				}
 			}
 		}
 	}
@@ -404,6 +458,8 @@
 }
 
 int *SEXPToIntArray (SEXP from_exp, unsigned int *count) {
+	RK_TRACE (RBACKEND);
+
 	int *integers;
 
 	// bad format? coerce the vector first
@@ -426,6 +482,8 @@
 }
 
 double *SEXPToRealArray (SEXP from_exp, unsigned int *count) {
+	RK_TRACE (RBACKEND);
+
 	double *reals;
 
 	// bad format? coerce the vector first
@@ -448,6 +506,8 @@
 }
 
 RData *SEXPToRData (SEXP from_exp) {
+	RK_TRACE (RBACKEND);
+
 	RData *data = new RData;
 
 	unsigned int count;
@@ -493,6 +553,8 @@
 }
 
 SEXP doError (SEXP call) {
+	RK_TRACE (RBACKEND);
+
 	unsigned int count;
 	QString *strings = SEXPToStringList (call, &count);
 	REmbedInternal::this_pointer->handleError (strings, count);
@@ -509,6 +571,8 @@
 } */
 
 SEXP doSubstackCall (SEXP call) {
+	RK_TRACE (RBACKEND);
+
 	unsigned int count;
 	QString *strings = SEXPToStringList (call, &count);
 	REmbedInternal::this_pointer->handleSubstackCall (strings, count);
@@ -517,6 +581,8 @@
 }
 
 bool REmbedInternal::startR (int argc, char** argv, size_t stacksize, void *stackstart) {
+	RK_TRACE (RBACKEND);
+
 	r_running = true;
 #ifdef R_2_3
 	Rf_initialize_R (argc, argv);
@@ -535,7 +601,19 @@
 #endif
 }
 
+SEXP doUpdateLocale () {
+	RK_TRACE (RBACKEND);
+
+	RK_DO (qDebug ("Changing locale"), RBACKEND, DL_WARNING);
+	REmbedInternal::this_pointer->current_locale_codec = RKGetCurrentLocaleCodec ();
+	RK_DO (qDebug ("New locale codec is %s", REmbedInternal::this_pointer->current_locale_codec->name ()), RBACKEND, DL_WARNING);
+
+	return R_NilValue;
+}
+
 bool REmbedInternal::registerFunctions (const char *library_path) {
+	RK_TRACE (RBACKEND);
+
 	DllInfo *info = R_getDllInfo (library_path);
 	if (!info) return false;
 
@@ -543,6 +621,7 @@
 //		{ "rk.do.condition", (DL_FUNC) &doCondition, 1 },
 		{ "rk.do.error", (DL_FUNC) &doError, 1 },
 		{ "rk.do.command", (DL_FUNC) &doSubstackCall, 1 },
+		{ "rk.update.locale", (DL_FUNC) &doUpdateLocale, 0 },
 		{ 0, 0, 0 }
 	};
 	R_registerRoutines (info, NULL, callMethods, NULL, NULL);
@@ -551,12 +630,15 @@
 }
 
 SEXP runCommandInternalBase (const QString &command_qstring, REmbedInternal::RKWardRError *error) {
+	RK_TRACE (RBACKEND);
+
 // some copying from RServe below
 	int r_error = 0;
 	ParseStatus status = PARSE_NULL;
 	SEXP cv, pr, exp;
 
-	QCString localc = command_qstring.local8Bit ();		// needed so the string below does not go out of scope
+	int len = -1;
+	QCString localc = REmbedInternal::this_pointer->current_locale_codec->fromUnicode (command_qstring, len);		// needed so the string below does not go out of scope
 	const char *command = localc;
 
 	PROTECT(cv=allocVector(STRSXP, 1));
@@ -637,6 +719,8 @@
 //TODO: this is not entirely correct. See PrintValueEnv (), which is what Repl_Console uses (but is hidden)
 */
 void tryPrintValue (SEXP exp, REmbedInternal::RKWardRError *error) {
+	RK_TRACE (RBACKEND);
+
 	int ierror = 0;
 	SEXP tryprint, e;
 
@@ -660,6 +744,8 @@
 
 #ifdef USE_R_REPLDLLDO1
 void runUserCommandInternal (void *) {
+	RK_TRACE (RBACKEND);
+
 /* R_ReplDLLdo1 return codes:
 -1: EOF
 1: normal prompt
@@ -676,6 +762,8 @@
 #endif
 
 void REmbedInternal::runCommandInternal (const QString &command_qstring, RKWardRError *error, bool print_result) {
+	RK_TRACE (RBACKEND);
+
 	connectCallbacks ();		// sorry, but we will not play nicely with additional frontends trying to override our callbacks. (Unless they start their own R event loop, then they should be fine)
 
 	if (!print_result) {
@@ -699,7 +787,8 @@
 		R_ReplDLLinit ();		// resets the parse buffer (things might be left over from a previous incomplete parse)
 		bool prev_iteration_was_incomplete = false;
 
-		QCString localc = command_qstring.local8Bit ();		// needed so the string below does not go out of scope
+		int len = -1;
+		QCString localc = current_locale_codec->fromUnicode (command_qstring, len);		// needed so the string below does not go out of scope
 		current_buffer = localc;
 
 		repldll_buffer_transfer_finished = false;
@@ -729,7 +818,6 @@
 		repldlldo1_wants_code = false;		// make sure we don't get confused in RReadConsole
 
 #else
-
 		R_Visible = (Rboolean) 0;
 
 		SEXP exp;
@@ -754,6 +842,8 @@
 }
 
 QString *REmbedInternal::getCommandAsStringVector (const QString &command, uint *count, RKWardRError *error) {	
+	RK_TRACE (RBACKEND);
+
 	SEXP exp;
 	QString *list = 0;
 	
@@ -773,6 +863,8 @@
 }
 
 double *REmbedInternal::getCommandAsRealVector (const QString &command, uint *count, RKWardRError *error) {
+	RK_TRACE (RBACKEND);
+
 	SEXP exp;
 	double *reals = 0;
 	
@@ -792,6 +884,8 @@
 }
 
 int *REmbedInternal::getCommandAsIntVector (const QString &command, uint *count, RKWardRError *error) {
+	RK_TRACE (RBACKEND);
+
 	SEXP exp;
 	int *integers = 0;
 	
@@ -811,6 +905,8 @@
 }
 
 RData *REmbedInternal::getCommandAsRData (const QString &command, RKWardRError *error) {
+	RK_TRACE (RBACKEND);
+
 	SEXP exp;
 	RData *data = 0;
 	
@@ -824,4 +920,3 @@
 	
 	return data;
 }
-

Modified: trunk/rkward/rkward/rbackend/rembedinternal.h
===================================================================
--- trunk/rkward/rkward/rbackend/rembedinternal.h	2007-03-11 13:51:43 UTC (rev 1557)
+++ trunk/rkward/rkward/rbackend/rembedinternal.h	2007-03-11 17:28:13 UTC (rev 1558)
@@ -165,10 +165,10 @@
 //	static bool output_is_warning;
 /** Flags used to classify output. */
 //	static bool next_output_is_error;
+	QTextCodec *current_locale_codec;
 private:
 // can't declare this as part of the class, as it would confuse REmbed
 //	SEXPREC *runCommandInternalBase (const char *command, bool *error);
-	QTextCodec *current_locale_codec;		// steal from QTextCodec::setupLocaleMapper to initialize
 };
  
 #endif

Modified: trunk/rkward/rkward/rbackend/rinterface.cpp
===================================================================
--- trunk/rkward/rkward/rbackend/rinterface.cpp	2007-03-11 13:51:43 UTC (rev 1557)
+++ trunk/rkward/rkward/rbackend/rinterface.cpp	2007-03-11 17:28:13 UTC (rev 1558)
@@ -357,6 +357,11 @@
 			MUTEX_UNLOCK;
 		}
 #endif // DISABLE_RKWINDOWCATCHER
+	} else if (call == "preLocaleChange") {
+		int res = KMessageBox::warningContinueCancel (0, i18n ("A command in the R backend is trying to change the character encoding. While RKWard offers support for this, and will try to adjust to the new locale, this operation may cause subtle bugs, if data windows are currently open. Also the feature is not well tested, yet, and it may be advisable to save your workspace before proceeding.\nIf you have any data editor opened, or in any doubt, it is recommended to close those first (this will probably be auto-detected in later versions of RKWard). In this case, please chose 'Cancel' now, then close the data windows, save, and retry."), i18n ("Locale change"));
+		if (res != KMessageBox::Continue) {
+			issueCommand (".rk.set.reply (FALSE)", RCommand::App | RCommand::Sync, QString::null, 0, 0, request->in_chain);
+		}
 	} else {
 		issueCommand (".rk.set.reply (\"Unrecognized call '" + call + "'. Ignoring\")", RCommand::App | RCommand::Sync, QString::null, 0, 0, request->in_chain);
 	}

Added: trunk/rkward/rkward/rbackend/rklocalesupport.cpp
===================================================================
--- trunk/rkward/rkward/rbackend/rklocalesupport.cpp	                        (rev 0)
+++ trunk/rkward/rkward/rbackend/rklocalesupport.cpp	2007-03-11 17:28:13 UTC (rev 1558)
@@ -0,0 +1,259 @@
+/***************************************************************************
+                          rklocalesupport  -  description
+                             -------------------
+    begin                : Sun Mar 11 2007
+    copyright            : (C) 2007 by Thomas Friedrichsmeier
+    email                : tfry at users.sourceforge.net
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#include "rklocalesupport.h"
+
+#include <qtextcodec.h>
+
+/* NOTE: This code in this file is an almost literal copy taken from setupLocaleMapper in qtextcodec.cpp in Qt 3.3.8 !*/
+
+QTextCodec *checkForCodec(const char *name) {
+    QTextCodec *c = QTextCodec::codecForName(name);
+    if (!c) {
+        const char *at = strchr(name, '@');
+        if (at) {
+            QCString n(name, at - name + 1);
+            c = QTextCodec::codecForName(n.data());
+        }
+    }
+    return c;
+}
+
+/* locale names mostly copied from XFree86 */
+static const char * const iso8859_2locales[] = {
+    "croatian", "cs", "cs_CS", "cs_CZ","cz", "cz_CZ", "czech", "hr",
+    "hr_HR", "hu", "hu_HU", "hungarian", "pl", "pl_PL", "polish", "ro",
+    "ro_RO", "rumanian", "serbocroatian", "sh", "sh_SP", "sh_YU", "sk",
+    "sk_SK", "sl", "sl_CS", "sl_SI", "slovak", "slovene", "sr_SP", 0 };
+
+static const char * const iso8859_3locales[] = {
+    "eo", 0 };
+
+static const char * const iso8859_4locales[] = {
+    "ee", "ee_EE", 0 };
+
+static const char * const iso8859_5locales[] = {
+    "mk", "mk_MK", "sp", "sp_YU", 0 };
+
+static const char * const cp_1251locales[] = {
+    "be", "be_BY", "bg", "bg_BG", "bulgarian", 0 };
+
+static const char * const pt_154locales[] = {
+    "ba_RU", "ky", "ky_KG", "kk", "kk_KZ", 0 };
+
+static const char * const iso8859_6locales[] = {
+    "ar_AA", "ar_SA", "arabic", 0 };
+
+static const char * const iso8859_7locales[] = {
+    "el", "el_GR", "greek", 0 };
+
+static const char * const iso8859_8locales[] = {
+    "hebrew", "he", "he_IL", "iw", "iw_IL", 0 };
+
+static const char * const iso8859_9locales[] = {
+    "tr", "tr_TR", "turkish", 0 };
+
+static const char * const iso8859_13locales[] = {
+    "lt", "lt_LT", "lv", "lv_LV", 0 };
+
+static const char * const iso8859_15locales[] = {
+    "et", "et_EE",
+    // Euro countries
+    "br_FR", "ca_ES", "de", "de_AT", "de_BE", "de_DE", "de_LU", "en_IE",
+    "es", "es_ES", "eu_ES", "fi", "fi_FI", "finnish", "fr", "fr_FR",
+    "fr_BE", "fr_LU", "french", "ga_IE", "gl_ES", "it", "it_IT", "oc_FR",
+    "nl", "nl_BE", "nl_NL", "pt", "pt_PT", "sv_FI", "wa_BE",
+    0 };
+
+static const char * const koi8_ulocales[] = {
+    "uk", "uk_UA", "ru_UA", "ukrainian", 0 };
+
+static const char * const tis_620locales[] = {
+    "th", "th_TH", "thai", 0 };
+
+static const char * const tcvnlocales[] = {
+    "vi", "vi_VN", 0 };
+
+static bool try_locale_list( const char * const locale[], const char * lang )
+{
+    int i;
+    for( i=0; locale[i] && *locale[i] && strcmp(locale[i], lang); i++ )
+        ;
+    return locale[i] != 0;
+}
+
+// For the probably_koi8_locales we have to look. the standard says
+// these are 8859-5, but almost all Russian users use KOI8-R and
+// incorrectly set $LANG to ru_RU. We'll check tolower() to see what
+// tolower() thinks ru_RU means.
+
+// If you read the history, it seems that many Russians blame ISO and
+// Perestroika for the confusion.
+//
+// The real bug is that some programs break if the user specifies
+// ru_RU.KOI8-R.
+
+static const char * const probably_koi8_rlocales[] = {
+    "ru", "ru_SU", "ru_RU", "russian", 0 };
+
+static QTextCodec * ru_RU_hack( const char * i ) {
+    QTextCodec * ru_RU_codec = 0;
+
+    QCString origlocale = setlocale( LC_CTYPE, i );
+    // unicode   koi8r   latin5   name
+    // 0x044E    0xC0    0xEE     CYRILLIC SMALL LETTER YU
+    // 0x042E    0xE0    0xCE     CYRILLIC CAPITAL LETTER YU
+    int latin5 = tolower( 0xCE );
+    int koi8r = tolower( 0xE0 );
+    if ( koi8r == 0xC0 && latin5 != 0xEE ) {
+        ru_RU_codec = QTextCodec::codecForName( "KOI8-R" );
+    } else if ( koi8r != 0xC0 && latin5 == 0xEE ) {
+        ru_RU_codec = QTextCodec::codecForName( "ISO 8859-5" );
+    } else {
+        // something else again... let's assume... *throws dice*
+        ru_RU_codec = QTextCodec::codecForName( "KOI8-R" );
+        qWarning( "QTextCodec: using KOI8-R, probe failed (%02x %02x %s)",
+                  koi8r, latin5, i );
+    }
+    setlocale( LC_CTYPE, origlocale.data() );
+
+    return ru_RU_codec;
+}
+
+QTextCodec *RKGetCurrentLocaleCodec () {
+
+	QTextCodec *localeMapper;
+
+#ifdef Q_OS_WIN32
+    localeMapper = QTextCodec::codecForName( "System" );
+#else
+
+#if defined (_XOPEN_UNIX) && !defined(Q_OS_QNX6) && !defined(Q_OS_OSF) && !defined(Q_OS_MAC)
+    char *charset = nl_langinfo (CODESET);
+    if ( charset )
+        localeMapper = QTextCodec::codecForName( charset );
+#endif
+
+    if ( !localeMapper ) {
+        // Very poorly defined and followed standards causes lots of code
+        // to try to get all the cases...
+
+        // Try to determine locale codeset from locale name assigned to
+        // LC_CTYPE category.
+
+        // First part is getting that locale name.  First try setlocale() which
+        // definitely knows it, but since we cannot fully trust it, get ready
+        // to fall back to environment variables.
+        char * ctype = qstrdup( setlocale( LC_CTYPE, 0 ) );
+
+        // Get the first nonempty value from $LC_ALL, $LC_CTYPE, and $LANG
+        // environment variables.
+        char * lang = qstrdup( getenv("LC_ALL") );
+        if ( !lang || lang[0] == 0 || strcmp( lang, "C" ) == 0 ) {
+            if ( lang ) delete [] lang;
+            lang = qstrdup( getenv("LC_CTYPE") );
+        }
+        if ( !lang || lang[0] == 0 || strcmp( lang, "C" ) == 0 ) {
+            if ( lang ) delete [] lang;
+            lang = qstrdup( getenv("LANG") );
+        }
+
+        // Now try these in order:
+        // 1. CODESET from ctype if it contains a .CODESET part (e.g. en_US.ISO8859-15)
+        // 2. CODESET from lang if it contains a .CODESET part
+        // 3. ctype (maybe the locale is named "ISO-8859-1" or something)
+        // 4. locale (ditto)
+        // 5. check for "@euro"
+        // 6. guess locale from ctype unless ctype is "C"
+        // 7. guess locale from lang
+
+        // 1. CODESET from ctype if it contains a .CODESET part (e.g. en_US.ISO8859-15)
+        char * codeset = ctype ? strchr( ctype, '.' ) : 0;
+        if ( codeset && *codeset == '.' )
+            localeMapper = checkForCodec( codeset + 1 );
+
+        // 2. CODESET from lang if it contains a .CODESET part
+        codeset = lang ? strchr( lang, '.' ) : 0;
+        if ( !localeMapper && codeset && *codeset == '.' ) 
+            localeMapper = checkForCodec( codeset + 1 );
+
+        // 3. ctype (maybe the locale is named "ISO-8859-1" or something)
+        if ( !localeMapper && ctype && *ctype != 0 && strcmp (ctype, "C") != 0 )
+            localeMapper = checkForCodec( ctype );
+
+        // 4. locale (ditto)
+        if ( !localeMapper && lang && *lang != 0 )
+            localeMapper = checkForCodec( lang );
+
+        // 5. "@euro"
+        if ( !localeMapper && ctype && strstr( ctype, "@euro" ) || lang && strstr( lang, "@euro" ) )
+            localeMapper = QTextCodec::codecForName( "ISO 8859-15" );
+
+        // 6. guess locale from ctype unless ctype is "C"
+        // 7. guess locale from lang
+        char * try_by_name = ctype;
+        if ( ctype && *ctype != 0 && strcmp (ctype, "C") != 0 )
+            try_by_name = lang;
+
+        // Now do the guessing.
+        if ( lang && *lang && !localeMapper && try_by_name && *try_by_name ) {
+            if ( try_locale_list( iso8859_15locales, lang ) )
+                localeMapper = QTextCodec::codecForName( "ISO 8859-15" );
+            else if ( try_locale_list( iso8859_2locales, lang ) )
+                localeMapper = QTextCodec::codecForName( "ISO 8859-2" );
+            else if ( try_locale_list( iso8859_3locales, lang ) )
+                localeMapper = QTextCodec::codecForName( "ISO 8859-3" );
+            else if ( try_locale_list( iso8859_4locales, lang ) )
+                localeMapper = QTextCodec::codecForName( "ISO 8859-4" );
+            else if ( try_locale_list( iso8859_5locales, lang ) )
+                localeMapper = QTextCodec::codecForName( "ISO 8859-5" );
+            else if ( try_locale_list( iso8859_6locales, lang ) )
+                localeMapper = QTextCodec::codecForName( "ISO 8859-6" );
+            else if ( try_locale_list( iso8859_7locales, lang ) )
+                localeMapper = QTextCodec::codecForName( "ISO 8859-7" );
+            else if ( try_locale_list( iso8859_8locales, lang ) )
+                localeMapper = QTextCodec::codecForName( "ISO 8859-8-I" );
+            else if ( try_locale_list( iso8859_9locales, lang ) )
+                localeMapper = QTextCodec::codecForName( "ISO 8859-9" );
+            else if ( try_locale_list( iso8859_13locales, lang ) )
+                localeMapper = QTextCodec::codecForName( "ISO 8859-13" );
+            else if ( try_locale_list( tis_620locales, lang ) )
+                localeMapper = QTextCodec::codecForName( "ISO 8859-11" );
+            else if ( try_locale_list( koi8_ulocales, lang ) )
+                localeMapper = QTextCodec::codecForName( "KOI8-U" );
+            else if ( try_locale_list( cp_1251locales, lang ) )
+                localeMapper = QTextCodec::codecForName( "CP 1251" );
+            else if ( try_locale_list( pt_154locales, lang ) )
+                localeMapper = QTextCodec::codecForName( "PT 154" );
+            else if ( try_locale_list( probably_koi8_rlocales, lang ) )
+                localeMapper = ru_RU_hack( lang );
+        }
+
+        delete [] ctype;
+        delete [] lang;
+    }
+    if ( localeMapper && localeMapper->mibEnum() == 11 )
+        localeMapper = QTextCodec::codecForName( "ISO 8859-8-I" );
+
+    // If everything failed, we default to 8859-1
+    // We could perhaps default to 8859-15.
+    if ( !localeMapper )
+        localeMapper = QTextCodec::codecForName( "ISO 8859-1" );
+#endif
+    return localeMapper;
+}
+

Added: trunk/rkward/rkward/rbackend/rklocalesupport.h
===================================================================
--- trunk/rkward/rkward/rbackend/rklocalesupport.h	                        (rev 0)
+++ trunk/rkward/rkward/rbackend/rklocalesupport.h	2007-03-11 17:28:13 UTC (rev 1558)
@@ -0,0 +1,26 @@
+/***************************************************************************
+                          rklocalesupport  -  description
+                             -------------------
+    begin                : Sun Mar 11 2007
+    copyright            : (C) 2007 by Thomas Friedrichsmeier
+    email                : tfry at users.sourceforge.net
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifndef RKLOCALESUPPORT_H
+#define RKLOCALESUPPORT_H
+
+class QTextCodec;
+
+/** Helper function to determine the QTextCodec best suited to recode the current CTYPE to UTF-8 */
+QTextCodec *RKGetCurrentLocaleCodec ();
+
+#endif

Modified: trunk/rkward/rkward/rbackend/rpackages/rkward/R/internal.R
===================================================================
--- trunk/rkward/rkward/rbackend/rpackages/rkward/R/internal.R	2007-03-11 13:51:43 UTC (rev 1557)
+++ trunk/rkward/rkward/rbackend/rpackages/rkward/R/internal.R	2007-03-11 17:28:13 UTC (rev 1558)
@@ -386,3 +386,19 @@
 		}
 	}
 }
+
+"Sys.setlocale" <- function (category = "LC_ALL", locale = "", ...) {
+	if (category == "LC_ALL" || category == "LC_CTYPE" || category == "LANG") {
+		.rk.do.call ("preLocaleChange", NULL);
+		if (!is.null (.rk.rkreply)) {
+			if (.rk.rkreply == FALSE) stop ("Changing the locale was cancelled by user");
+		}
+
+		ret <- base::Sys.setlocale (category, locale, ...)
+
+		.Call ("rk.update.locale")
+		ret
+	} else {
+		base::Sys.setlocale (category, locale, ...)
+	}
+}


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.




More information about the rkward-tracker mailing list