diff --git a/harbour/ChangeLog b/harbour/ChangeLog index 86f45dce08..8ee47ef21a 100644 --- a/harbour/ChangeLog +++ b/harbour/ChangeLog @@ -8,6 +8,243 @@ 2008-12-31 13:59 UTC+0100 Foo Bar (foo.bar foobar.org) */ +2008-12-11 19:48 UTC+0100 Przemyslaw Czerpak (druzus/at/priv.onet.pl) + * harbour/include/hbthread.h + * harbour/source/vm/thread.c + + added hb_atomic_set(), hb_atomic_get(), hb_atomic_inc() and + hb_atomic_dec() functions which operates on HB_COUNTER or smaller + type if it's necessary for some platforms which can be access/assign + increment/decrement in MT safe atom operations. + hb_atomic_dec() returns true if counter is 0 after decrementation + + * harbour/include/hbatomic.h + ! fixed compilation in Linux and OpenWatcom + + * harbour/include/hbapiitm.h + * harbour/source/rtl/itemseri.c + + make hb_itemSerialize() and hb_itemDeserialize() public functions + ! fixed serialization items with internal item references + + * harbour/source/vm/hvm.c + * release memvars after closing RDDs + + * harbour/source/debug/dbgentry.c + ! fixed buffer overflow reported by Rodrigo + + * harbour/source/vm/macro.c + * harbour/source/compiler/hbmain.c + * formatting + + * harbour/include/hbexprb.c + ! fixed wrongly recognized functions with HB_I18N_ prefix as + HB_I18N_GETTEXT() + + * harbour/include/hbapi.h + * harbour/include/hbstack.h + * harbour/include/hbthread.h + * harbour/source/vm/estack.c + * harbour/source/vm/thread.c + * harbour/source/vm/hvm.c + + added support for I18N in HVM. + Each thread can have it's own i18n set. + When new thread is created then it inherits i18n set from parent + thread and both uses the same set (please remember about it if you + will want to make some direct modifications on active i18n set + internals). + When thread change active i18n set then it effects only this thread + and new threads which will be create later. It does not change i18n + in other existing threads. + + added functions to set/get pointer to active i18n set in HVM + void * hb_vmI18N( void ) + void hb_vmSetI18N( void * ) + + * harbour/include/hbapi.h + * harbour/source/rtl/hbi18n.c + + added i18n module. Now only for internal Harbour usage without support + for optional switching to alternative implementations. + I'll add such functionality later when I will work on native gettext + support. + + The following public .prg functions has been added: + HB_I18N_GETTEXT[_STRICT]( [, ] ) + -> | + HB_I18N_NGETTEXT[_STRICT]( , | ; + [, ] ) + -> | | [ ] + This is minimal support necessary for .prg code which has to exists + in each i18n module working with Harbour. + + The following functions had been added as public C API: + PHB_ITEM hb_i18n_gettext( PHB_ITEM pMsgID, PHB_ITEM pContext ) + PHB_ITEM hb_i18n_ngettext( PHB_ITEM pNum, + PHB_ITEM pMsgID, PHB_ITEM pContext ) + + The following functions had been added as private HVM C API: + void hb_i18n_init( void ) + void hb_i18n_exit( void ) + void hb_i18n_release( void * cargo ) + void * hb_i18n_alloc( void * cargo ) + They have to be supported by alternative i18n modules + + The following functions has been added to manage Harbour i18n + translations sets: + + HB_I18N_CREATE() + -> + Creates new empty I18N translation set + + HB_I18N_CODEPAGE( [,] [], [], [] ) + -> + Gets or sets Harbour codepage used by translation set + - I18N translation set, + if it's not given then currently active I18N set is used + - new CP ID. Must be linked with application + - when it's .T. then get/set base massages CP instead of + translated massages CP + - if it's .T. then translate base (==.T.) or + final messages in I18N set from previous CP to + given one. Base messages translation in synced + with context ID translation. + + HB_I18N_PLURALFORM( [,] [|], [] ) + -> | + Gets or sets plural form used for final or base messages + - I18N translation set, + if it's not given then currently active I18N set is used + - language ID of plural form, f.e.: "EN", "PL", "LT". + Now only three above are supported. Please add rules + for other languages to source/rtl/hbi18n.c. + - codeblock used to calculate plural form indexes. + can be used instead of character representation but + it's not storred in serialized I18N set + - when it's .T. then get/set base massages plural form + instead of translated massages one. + + HB_I18N_DESCRIPTION( [,] [] ) + -> + Gets or sets translation set description. After serialization + up to 32 bytes is stored in header which can be easy used to + determinate type of translation file. + - I18N translation set, + if it's not given then currently active I18N set is used + - new description + + HB_I18N_ADDTEXT( , , | [, ] ) + -> NIL + Adds new message with translation to i18n translation set + - I18N translation set + - original message + - translated message + - array with translated messages used for plural forms + - message context + + HB_I18N_SET( [ | NIL ] ) + -> + Sets given I18N translation set as default one used by + HB_I18N_[N]GETTEXT[_STRICT]() functions or remove translation + set for calling thread when passed parameter is NIL + - I18N translation set + Returns logical value which is .T. when i18n set is active + + HB_I18N_SAVETABLE( [] ) + -> + Returns I18N translation as string item which can be stored + in file or database + - I18N translation set, if it's not given then currently + active I18N set is used + + HB_I18N_RESTORETABLE( ) + -> | NIL + Restores I18N translation set from strin item. + - I18N translation set in string representation + On success it returns new set otherwise NIL if + is not valid item created by HB_I18N_SAVETABLE() or it's corrupted. + + HB_I18N_HEADERSIZE() + -> + Returns size of header used by i18n serialized version + + HB_I18N_CHEK( | [, @ ] ) + -> + - i18n translation set serialized by HB_I18N_SAVETABLE + - header of i18n translation set + ( LEFT( , HB_I18N_HEADERSIZE() ) + - optional parameter passed by reference where + will be sored i18n translation set description + extracted from valid header + Returns logical value indicating if given table or header is + valid serialized by HB_I18N_SAVETABLE() data. It does not + decode the table though it validates size and control sums. + + These functions are optional and some future alternative implementations + may not support all of them and/or may provide some other functions. + + + added unofficial .prg function __I18N_HASHTABLE() which allows to + access hash table used by i18n translation set or create new translation + set with given hash table. It's helper functions for developers which + will work on Harbour i18n tools and should not be used by Harbour users. + + Unlike original gettext Harbour allows to use language with many + plural forms as base one. In such case programmer should activate + at application startup default i18n translation set with base plural + form valid for base application language, f.e. by: + pI18N := hb_i18n_create() + hb_i18n_pluralForm( pI18N, | , .t. ) + hb_i18n_set( pI18N ) + .prg code example: + + #xtranslate _( ) => hb_i18n_gettext_strict( ) + #xtranslate _N( ) => hb_i18n_ngettext_strict( ) + + proc main() + local pI18N, i + + pI18N := hb_i18n_create() + hb_i18n_pluralForm( pI18N, "PL", .t. ) + hb_i18n_set( pI18N ) + + for i := 0 to 30 + ? i, _N( i, {"grosz", "grosze", "groszy"} ) + if i > 0 .and. i % 10 == 0 + wait + endif + next + return + + In .pot files created during compilation by Harbour with -j option + for above code we have the following entries for message with plural + forms: + msgid "grosz" + msgid_plural "grosze" + msgid_plural2 "groszy" + msgstr[0] "" + The msgid_plural2 (and others if language has more plural forms) + is Harbour extension which is not gettext compatible. + + + The above implementation is base version but should be fully functional. + Now we will need functions to safe/read i18n files and tools to mange + .pot files: merge them, edit translations, create final binary i18n + translation sets. Because we are using gettext compatible .pot files + then for some of such jobs we can use original gettext tools but we + need at least function which will create translation set from one or + more .pot files. + We should also agree some default localization(s) for files containing + translated data, their name convention and environment variable(s) + to set default language. It's not strictly necessary and each user + can have his own implementation but it would help in adding new + translations by final users to any Harbour application which will + respect them. We can use LANG envvar to extract preferred language + and use the same path as executed application looking for files + -.hil files though it may create some problems for + OSes which support only 8.3 file names so we can also define that + HB_I18N envvar has higher priority and points to expected translation + file. + + * harbour/include/hbextern.ch + - removed old __i18n_*() functions + + added current i18n functions + 2008-12-09 12:46 UTC-0800 Pritpal Bedi (pritpal@vouchcac.com) * harbour/contrib/gtwvg/makefile * harbour/contrib/gtwvg/common.mak diff --git a/harbour/include/hbapi.h b/harbour/include/hbapi.h index 7d3449860f..9a0024438a 100644 --- a/harbour/include/hbapi.h +++ b/harbour/include/hbapi.h @@ -1001,6 +1001,19 @@ extern void hb_idleState( void ); /* services a single idle state */ extern void hb_idleReset( void ); /* services a single idle state */ extern void hb_idleSleep( double dSeconds ); /* sleep for a given time serving idle task */ +/* I18N public API */ +extern PHB_ITEM hb_i18n_ngettext( PHB_ITEM pNum, PHB_ITEM pMsgID, PHB_ITEM pContext ); +extern PHB_ITEM hb_i18n_gettext( PHB_ITEM pMsgID, PHB_ITEM pContext ); +/* I18N internal HVM API */ +#if defined( _HB_API_INTERNAL_ ) || defined( _HB_I18N_INTERNAL_ ) +extern void * hb_vmI18N( void ); +extern void hb_vmSetI18N( void * ); +extern void hb_i18n_init( void ); +extern void hb_i18n_exit( void ); +extern void hb_i18n_release( void * cargo ); +extern void * hb_i18n_alloc( void * cargo ); +#endif /* _HB_API_INTERNAL_ || _HB_I18N_INTERNAL_ */ + /* misc */ extern HB_EXPORT char * hb_verPlatform( void ); /* retrieves a newly allocated buffer containing platform version */ extern HB_EXPORT char * hb_verCompiler( void ); /* retrieves a newly allocated buffer containing compiler version */ diff --git a/harbour/include/hbapiitm.h b/harbour/include/hbapiitm.h index 6f21fca18d..a528944138 100644 --- a/harbour/include/hbapiitm.h +++ b/harbour/include/hbapiitm.h @@ -178,6 +178,9 @@ extern HB_EXPORT PHB_ITEM hb_itemValToStr ( PHB_ITEM pItem ); /* Convert any extern HB_EXPORT char * hb_itemPadConv ( PHB_ITEM pItem, ULONG * pulSize, BOOL * bFreeReq ); extern HB_EXPORT void hb_itemSwap ( PHB_ITEM pItem1, PHB_ITEM pItem2 ); +extern HB_EXPORT char * hb_itemSerialize( PHB_ITEM pItem, BOOL fNumSize, ULONG *pulSize ); +extern HB_EXPORT PHB_ITEM hb_itemDeserialize( const char ** pBufferPtr, ULONG * pulSize ); + #if defined( _HB_API_INTERNAL_ ) # define hb_itemSetNil( item ) do { \ diff --git a/harbour/include/hbatomic.h b/harbour/include/hbatomic.h index 02608767a6..79f14d14d0 100644 --- a/harbour/include/hbatomic.h +++ b/harbour/include/hbatomic.h @@ -63,7 +63,7 @@ #if defined( __SVR4 ) # include #endif -#if defined( HB_OS_UNIX ) +#if defined( HB_OS_UNIX ) && !defined( __WATCOMC__ ) # include #endif @@ -258,7 +258,7 @@ HB_EXTERN_BEGIN # endif /* ???CPU?? */ -#endif +#endif /* __GNUC__ */ #if defined( HB_OS_WIN_32 ) @@ -332,7 +332,7 @@ HB_EXTERN_BEGIN # define HB_SPINLOCK_RELEASE(l) OSSpinLockUnlock(l) # endif -#endif +#endif /* HB_OS_??? */ HB_EXTERN_END diff --git a/harbour/include/hbexprb.c b/harbour/include/hbexprb.c index 56b024f96b..8578a37a87 100644 --- a/harbour/include/hbexprb.c +++ b/harbour/include/hbexprb.c @@ -1770,10 +1770,10 @@ static HB_EXPR_FUNC( hb_compExprUseFunCall ) else if( strncmp( "HB_I18N_", pName->value.asSymbol, 8 ) == 0 ) { HB_EXPR_PTR pArg = pParms->value.asList.pExprList, pCount = NULL; - BOOL fStrict, fNoop, fPlural, fUnknown; + BOOL fStrict, fNoop, fPlural, fI18nFunc; ULONG ulPos = 8; - fStrict = fNoop = fPlural = fUnknown = FALSE; + fStrict = fNoop = fPlural = fI18nFunc = FALSE; if( pName->value.asSymbol[ ulPos ] == 'N' ) { fPlural = TRUE; @@ -1786,16 +1786,14 @@ static HB_EXPR_FUNC( hb_compExprUseFunCall ) { ++ulPos; if( strcmp( "STRICT", &pName->value.asSymbol[ ulPos ] ) == 0 ) - fStrict = TRUE; + fI18nFunc = fStrict = TRUE; else if( strcmp( "NOOP", &pName->value.asSymbol[ ulPos ] ) == 0 ) - fNoop = TRUE; - else - fUnknown = TRUE; + fI18nFunc = fNoop = TRUE; } - else if( pName->value.asSymbol[ ulPos ] ) - fUnknown = TRUE; + else if( !pName->value.asSymbol[ ulPos ] ) + fI18nFunc = TRUE; } - if( !fUnknown ) + if( fI18nFunc ) { int iWarning = 0; HB_EXPR_PTR pBadParam = NULL; diff --git a/harbour/include/hbextern.ch b/harbour/include/hbextern.ch index 813dc1487e..ef7350b8b8 100644 --- a/harbour/include/hbextern.ch +++ b/harbour/include/hbextern.ch @@ -543,10 +543,23 @@ EXTERNAL __MVXRELEASE EXTERNAL __EINSTVAR52 EXTERNAL __EINSTVAR53 -EXTERNAL __I18N_SAVE -EXTERNAL __I18N_LOAD -EXTERNAL __I18N_LOADFROMMEMORY -EXTERNAL __I18N_GETTEXT +/* i18n */ +EXTERNAL HB_I18N_GETTEXT +EXTERNAL HB_I18N_GETTEXT_STRICT +EXTERNAL HB_I18N_NGETTEXT +EXTERNAL HB_I18N_NGETTEXT_STRICT + +EXTERNAL HB_I18N_CREATE +EXTERNAL HB_I18N_CODEPAGE +EXTERNAL HB_I18N_PLURALFORM +EXTERNAL HB_I18N_DESCRIPTION +EXTERNAL HB_I18N_ADDTEXT +EXTERNAL HB_I18N_SET +EXTERNAL HB_I18N_SAVETABLE +EXTERNAL HB_I18N_RESTORETABLE +EXTERNAL HB_I18N_HEADERSIZE +EXTERNAL HB_I18N_CHECK +EXTERNAL __I18N_HASHTABLE /* The debugger interface */ diff --git a/harbour/include/hbstack.h b/harbour/include/hbstack.h index f11f1d0805..1d1ab2c0c5 100644 --- a/harbour/include/hbstack.h +++ b/harbour/include/hbstack.h @@ -168,6 +168,7 @@ typedef struct char szDate[ 9 ]; /* last returned date from _pards() yyyymmdd format */ void * pCDP; /* current codepage module */ void * pLang; /* current language module */ + void * pI18N; /* current I18N module */ void * hGT; /* current GT module */ int iTSD; /* number of allocated TSD holders */ PHB_TSD_HOLDER pTSD; /* thread specific data holder */ @@ -322,6 +323,8 @@ extern void * hb_stackGetCDP( void ); extern void hb_stackSetCDP( void * ); extern void * hb_stackGetLang( void ); extern void hb_stackSetLang( void * ); +extern void * hb_stackGetI18N( void ); +extern void hb_stackSetI18N( void * ); extern void hb_stackIsStackRef( void *, PHB_TSD_FUNC ); @@ -369,6 +372,8 @@ extern void hb_stackIsStackRef( void *, PHB_TSD_FUNC ); #define hb_stackSetCDP( p ) do { hb_stack.pCDP = ( p ); } while ( 0 ) #define hb_stackGetLang( ) ( hb_stack.pLang ) #define hb_stackSetLang( p ) do { hb_stack.pLang = ( p ); } while ( 0 ) +#define hb_stackGetI18N( ) ( hb_stack.pI18N ) +#define hb_stackSetI18N( p ) do { hb_stack.pI18N = ( p ); } while ( 0 ) #define hb_stackId( ) ( ( void * ) &hb_stack ) #if defined( HB_MT_VM ) diff --git a/harbour/include/hbthread.h b/harbour/include/hbthread.h index 28eac1a636..34edb9d271 100644 --- a/harbour/include/hbthread.h +++ b/harbour/include/hbthread.h @@ -275,6 +275,7 @@ typedef struct _HB_THREADSTATE const char * pszLang; const char * pszDefRDD; PHB_SET_STRUCT pSet; + void * pI18N; void * hGT; void * pStackId; BOOL fActive; @@ -295,10 +296,17 @@ extern void hb_threadExit( void ); extern PHB_THREADSTATE hb_threadStateNew( void ); +/* atomic oprtations */ +void hb_atomic_set( volatile HB_COUNTER * pCounter, HB_COUNTER value ); +HB_COUNTER hb_atomic_get( volatile HB_COUNTER * pCounter ); +void hb_atomic_inc( volatile HB_COUNTER * pCounter ); +BOOL hb_atomic_dec( volatile HB_COUNTER * pCounter ); /* returns TRUE when counter reach after decrementation */ + /* Critical sections or fast non recursive MUTEXes */ extern void hb_threadEnterCriticalSection( HB_CRITICAL_T * critical ); extern void hb_threadLeaveCriticalSection( HB_CRITICAL_T * critical ); +/* conditional variables */ extern BOOL hb_threadCondSignal( HB_COND_T * cond ); extern BOOL hb_threadCondBroadcast( HB_COND_T * cond ); extern BOOL hb_threadCondWait( HB_COND_T * cond, HB_CRITICAL_T * mutex ); diff --git a/harbour/source/compiler/hbmain.c b/harbour/source/compiler/hbmain.c index 7c96a6d9be..94a2b3efd9 100644 --- a/harbour/source/compiler/hbmain.c +++ b/harbour/source/compiler/hbmain.c @@ -2388,7 +2388,7 @@ void hb_compGenStaticName( const char *szVarName, HB_COMP_DECL ) BYTE bGlobal = 0; PFUNCTION pFunc; int iVar; - + if( ! HB_COMP_PARAM->fStartProc && HB_COMP_PARAM->functions.iCount <= 1 ) { /* Variable declaration is outside of function/procedure body. diff --git a/harbour/source/debug/dbgentry.c b/harbour/source/debug/dbgentry.c index b241aa4822..fff48dffed 100644 --- a/harbour/source/debug/dbgentry.c +++ b/harbour/source/debug/dbgentry.c @@ -1014,7 +1014,7 @@ static int hb_dbgEvalSubstituteVar( HB_WATCHPOINT *watch, char *szWord, int nSta t[ nStart + 6 ] = '0' + ( char ) ( ( j + 1 ) / 10 ); t[ nStart + 7 ] = '0' + ( char ) ( ( j + 1 ) % 10 ); t[ nStart + 8 ] = ']'; - hb_strncpy( t + nStart + 9, watch->szExpr + nStart + nLen, strlen( watch->szExpr ) - nLen + 1 - nStart ); + hb_strncpy( t + nStart + 9, watch->szExpr + nStart + nLen, strlen( watch->szExpr ) - nLen - nStart ); FREE( watch->szExpr ); watch->szExpr = t; return nStart + 9; diff --git a/harbour/source/rtl/hbi18n.c b/harbour/source/rtl/hbi18n.c index 5af67da691..7412a526a6 100644 --- a/harbour/source/rtl/hbi18n.c +++ b/harbour/source/rtl/hbi18n.c @@ -4,13 +4,10 @@ /* * Harbour Project source code: - * __I18N low-level Harbour functions + * I18N translation functions Harbour functions * - * Copyright 2008 Viktor Szakats + * Copyright 2008 Przemyslaw Czerpak * www - http://www.harbour-project.org - * Loosely based on work by: - * Copyright 2003 Giancarlo Niccolai - * www - http://www.xharbour.org * * 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 @@ -53,242 +50,1020 @@ * */ +#define _HB_I18N_INTERNAL_ + #include "hbapi.h" -#include "hbapifs.h" #include "hbapiitm.h" #include "hbapierr.h" +#include "hbapicdp.h" +#include "hbmath.h" +#include "hbvm.h" +#include "hbstack.h" +#include "hbthread.h" +#include "hbchksum.h" -static const BYTE s_szHead[ 4 ] = { 193, 'H', 'B', 'L' }; +/* + i18n hash table items: -static BOOL hb_i18n_save( HB_FHANDLE handle, PHB_ITEM pTable ) + "LANG" => + "BASE_LANG" => + "CODEPAGE" => + "BASE_CODEPAGE" => + "DESCRIPTION" => + "PLURAL_EXP" => + "BASE_PLURAL_EXP" => + "CONTEXT" => ; + { "" => { => | } ; // default context + [, => { => | } ] } + + i18n file format: + + 00-03 signature { 193, 'H', 'B', 'L' } + 04-07 size of serialized i18n hash table, 32 bits in little endian order + 08-11 CRC32 of serialized i18n hash table + 12-15 unused + 16-47 description + 48-63 unused + 64-.. serialized i18n hash table + */ + +#define HB_I18N_HEADER_SIZE 64 +#define HB_I18N_SIG_OFFSET 0 +#define HB_I18N_SIG_SIZE 4 +#define HB_I18N_SIZE_OFFSET 4 +#define HB_I18N_CRC_OFFSET 8 +#define HB_I18N_TXT_OFFSET 16 +#define HB_I18N_TXT_SIZE 32 + +#define HB_I18N_PLURAL_EN 1 +#define HB_I18N_PLURAL_PL 2 +#define HB_I18N_PLURAL_LT 3 + +typedef struct _HB_PLURAL_FORMS { - ULONG i, j; - BYTE buffer[ 4 ]; + const char * szLangID; + int iForm; +} +HB_PLURAL_FORMS, *PHB_PLURAL_FORMS; - HB_PUT_LE_UINT32( buffer, ( UINT32 ) hb_arrayLen( pTable ) ); +static const HB_PLURAL_FORMS s_plural_forms[] = +{ + { "EN", HB_I18N_PLURAL_EN }, + { "PL", HB_I18N_PLURAL_PL }, + { "LT", HB_I18N_PLURAL_LT } +}; - if( hb_fsWrite( handle, ( BYTE * ) s_szHead, sizeof( s_szHead ) ) != sizeof( s_szHead ) || - hb_fsWrite( handle, buffer, 4 ) != 4 ) +#define HB_PLURAL_FOMRS_COUNT ( sizeof( s_plural_forms ) / sizeof( HB_PLURAL_FORMS ) ) + +static const UCHAR s_signature[ 4 ] = { 193, 'H', 'B', 'L' }; +typedef struct _HB_I18N_TRANS +{ + HB_COUNTER iUsers; + PHB_CODEPAGE cdpage; + PHB_CODEPAGE base_cdpage; + PHB_ITEM table; + PHB_ITEM context_table; + PHB_ITEM default_context; + PHB_ITEM plural_block; + PHB_ITEM base_plural_block; + int plural_form; + int base_plural_form; +} +HB_I18N_TRANS, * PHB_I18N_TRANS; + +static PHB_I18N_TRANS hb_i18n_table( void ) +{ + return ( PHB_I18N_TRANS ) hb_vmI18N(); +} + +static int hb_i18n_pluralformfind( const char * szLang ) +{ + int i; + + for( i = 0; i < ( int ) HB_PLURAL_FOMRS_COUNT; ++i ) + { + if( hb_stricmp( szLang, s_plural_forms[ i ].szLangID ) == 0 ) + return s_plural_forms[ i ].iForm; + } + if( strlen( szLang ) > 2 ) + { + for( i = 0; i < ( int ) HB_PLURAL_FOMRS_COUNT; ++i ) + { + if( hb_strnicmp( szLang, s_plural_forms[ i ].szLangID, 2 ) == 0 ) + return s_plural_forms[ i ].iForm; + } + } + return 0; +} + +static const char * hb_i18n_pluralformid( int iForm ) +{ + int i; + + for( i = 0; i < ( int ) HB_PLURAL_FOMRS_COUNT; ++i ) + { + if( s_plural_forms[ i ].iForm == iForm ) + return s_plural_forms[ i ].szLangID; + } + return NULL; +} + +static long hb_i18n_pluralindex( int iForm, PHB_ITEM pNum ) +{ + double n = hb_numRound( hb_itemGetND( pNum ), 10 ), n10, n100; + + switch( iForm ) + { + case HB_I18N_PLURAL_PL: + n10 = fmod( n, 10.0 ); + n100 = fmod( n, 100.0 ); + return n == 1 ? 1 : ( n10 >= 2 && n10 <= 4 && + ( n100 < 10 || n100 >= 20 ) ? 2 : 3 ); + + case HB_I18N_PLURAL_LT: + n10 = fmod( n, 10.0 ); + n100 = fmod( n, 100.0 ); + return n100 >= 10 && n100 < 20 ? 2 : ( n10 == 1 ? 1 : 3 ); + + case HB_I18N_PLURAL_EN: + default: + return n == 1 ? 1 : 2; + } +} + +static void hb_i18n_setitem( PHB_ITEM pHash, const char * szKey, const char * szValue ) +{ + PHB_ITEM pKey = hb_itemPutC( NULL, szKey ); + PHB_ITEM pValue = hb_itemPutC( NULL, szValue ); + hb_hashAdd( pHash, pKey, pValue ); + hb_itemRelease( pKey ); + hb_itemRelease( pValue ); +} + +static PHB_ITEM hb_i18n_pluralexp_compile( PHB_ITEM pExp ) +{ + ULONG ulLen = hb_itemGetCLen( pExp ); + PHB_ITEM pBlock = NULL; + + if( ulLen > 0 ) + { + char * szMacro = ( char * ) hb_xgrab( ulLen + 6 ); + const char * szType; + PHB_ITEM pMacro; + + szMacro[ 0 ] = '{'; + szMacro[ 1 ] = '|'; + szMacro[ 2 ] = 'n'; + szMacro[ 3 ] = '|'; + memcpy( &szMacro[ 4 ], hb_itemGetCPtr( pExp ), ulLen ); + szMacro[ 4 + ulLen ] = '}'; + szMacro[ 5 + ulLen ] = '\0'; + pMacro = hb_itemPutCPtr( NULL, szMacro, ulLen ); + szType = hb_macroGetType( pMacro ); + if( *szType == 'B' ) + { + hb_vmPush( pMacro ); + hb_macroGetValue( hb_stackItemFromTop( -1 ), 0, 0 ); + if( hb_vmRequestQuery() == 0 ) + { + pExp = hb_stackItemFromTop( -1 ); + if( HB_IS_BLOCK( pExp ) ) + pBlock = hb_itemNew( pExp ); + hb_stackPop(); + } + } + hb_itemRelease( pMacro ); + } + + return pBlock; +} + +static PHB_I18N_TRANS hb_i18n_new( void ) +{ + PHB_I18N_TRANS pI18N; + PHB_ITEM pKey; + + pI18N = ( PHB_I18N_TRANS ) memset( hb_xgrab( sizeof( HB_I18N_TRANS ) ), + 0, sizeof( HB_I18N_TRANS ) ); + hb_atomic_set( &pI18N->iUsers, 1 ); + pI18N->table = hb_hashNew( hb_itemNew( NULL ) ); + pI18N->context_table = hb_hashNew( hb_itemNew( NULL ) ); + pI18N->default_context = hb_hashNew( hb_itemNew( NULL ) ); + pKey = hb_itemPutC( NULL, "CONTEXT" ); + hb_hashAdd( pI18N->table, pKey, pI18N->context_table ); + pKey = hb_itemPutC( pKey, "" ); + hb_hashAdd( pI18N->context_table, pKey, pI18N->default_context ); + hb_itemRelease( pKey ); + + return pI18N; +} + +/* HVM init */ +void hb_i18n_init( void ) +{ + /* do nothing in this implementation */ +} + +/* HVM exit */ +void hb_i18n_exit( void ) +{ + /* do nothing in this implementation */ +} + +/* make copy of i18n set for new thread */ +void * hb_i18n_alloc( void * cargo ) +{ + if( cargo ) + hb_atomic_inc( &( ( PHB_I18N_TRANS ) cargo )->iUsers ); + return cargo; +} + +/* release i18n set when thread is terminated */ +void hb_i18n_release( void * cargo ) +{ + if( cargo ) + { + PHB_I18N_TRANS pI18N = ( PHB_I18N_TRANS ) cargo; + + if( hb_atomic_dec( &pI18N->iUsers ) ) + { + if( pI18N->table ) + hb_itemRelease( pI18N->table ); + if( pI18N->context_table ) + hb_itemRelease( pI18N->context_table ); + if( pI18N->default_context ) + hb_itemRelease( pI18N->default_context ); + if( pI18N->base_plural_block ) + hb_itemRelease( pI18N->base_plural_block ); + if( pI18N->plural_block ) + hb_itemRelease( pI18N->plural_block ); + hb_xfree( pI18N ); + } + } +} + +static PHB_I18N_TRANS hb_i18n_initialize( PHB_ITEM pTable ) +{ + PHB_I18N_TRANS pI18N = NULL; + + if( HB_IS_HASH( pTable ) ) + { + PHB_ITEM pKey, pContext, pDefContext = NULL, pValue; + + pKey = hb_itemPutC( NULL, "CONTEXT" ); + pContext = hb_hashGetItemPtr( pTable, pKey, 0 ); + if( pContext ) + { + pKey = hb_itemPutC( pKey, "" ); + pDefContext = hb_hashGetItemPtr( pContext, pKey, 0 ); + } + + if( pContext && pDefContext ) + { + pI18N = ( PHB_I18N_TRANS ) memset( hb_xgrab( sizeof( HB_I18N_TRANS ) ), + 0, sizeof( HB_I18N_TRANS ) ); + hb_atomic_set( &pI18N->iUsers, 1 ); + pI18N->table = pTable; + pI18N->context_table = hb_itemNew( pContext ); + pI18N->default_context = hb_itemNew( pDefContext ); + + pKey = hb_itemPutC( pKey, "BASE_CODEPAGE" ); + pValue = hb_hashGetItemPtr( pTable, pKey, 0 ); + if( pValue ) + pI18N->base_cdpage = hb_cdpFind( hb_itemGetCPtr( pValue ) ); + + pKey = hb_itemPutC( pKey, "CODEPAGE" ); + pValue = hb_hashGetItemPtr( pTable, pKey, 0 ); + if( pValue ) + pI18N->cdpage = hb_cdpFind( hb_itemGetCPtr( pValue ) ); + + pKey = hb_itemPutC( pKey, "BASE_LANG" ); + pValue = hb_hashGetItemPtr( pTable, pKey, 0 ); + if( pValue ) + pI18N->base_plural_form = hb_i18n_pluralformfind( hb_itemGetCPtr( pValue ) ); + + pKey = hb_itemPutC( pKey, "LANG" ); + pValue = hb_hashGetItemPtr( pTable, pKey, 0 ); + if( pValue ) + pI18N->plural_form = hb_i18n_pluralformfind( hb_itemGetCPtr( pValue ) ); + + pKey = hb_itemPutC( pKey, "BASE_PLURAL_EXP" ); + pValue = hb_hashGetItemPtr( pTable, pKey, 0 ); + if( pValue ) + pI18N->base_plural_block = hb_i18n_pluralexp_compile( pValue ); + + pKey = hb_itemPutC( pKey, "PLURAL_EXP" ); + pValue = hb_hashGetItemPtr( pTable, pKey, 0 ); + if( pValue ) + pI18N->plural_block = hb_i18n_pluralexp_compile( pValue ); + } + hb_itemRelease( pKey ); + } + + return pI18N; +} + +static PHB_ITEM hb_i18n_serialize( PHB_I18N_TRANS pI18N ) +{ + if( pI18N ) + { + ULONG ulSize, ulCRC; + char * pBuffer = hb_itemSerialize( pI18N->table, FALSE, &ulSize ); + char * pI18Nbuffer; + PHB_ITEM pKey, pValue; + + ulCRC = hb_crc32( 0, ( const BYTE * ) pBuffer, ulSize ); + pI18Nbuffer = ( char * ) memset( hb_xgrab( ulSize + HB_I18N_HEADER_SIZE + 1 ), + 0, HB_I18N_HEADER_SIZE ); + memcpy( pI18Nbuffer + HB_I18N_HEADER_SIZE, pBuffer, ulSize ); + hb_xfree( pBuffer ); + + memcpy( pI18Nbuffer, s_signature, HB_I18N_SIG_SIZE ); + HB_PUT_LE_UINT32( &pI18Nbuffer[ HB_I18N_SIZE_OFFSET ], ulSize ); + HB_PUT_LE_UINT32( &pI18Nbuffer[ HB_I18N_CRC_OFFSET ], ulCRC ); + + pKey = hb_itemPutC( NULL, "DESCRIPTION" ); + pValue = hb_hashGetItemPtr( pI18N->table, pKey, 0 ); + if( pValue ) + hb_strncpy( &pI18Nbuffer[ HB_I18N_TXT_OFFSET ], + hb_itemGetCPtr( pValue ), HB_I18N_TXT_SIZE ); + + return hb_itemPutCLPtr( pKey, pI18Nbuffer, ulSize + HB_I18N_HEADER_SIZE ); + } + + return NULL; +} + +static BOOL hb_i18n_headercheck( const char * pBuffer, ULONG ulLen ) +{ + if( ulLen < HB_I18N_HEADER_SIZE ) return FALSE; - for( i = 1; i <= hb_arrayLen( pTable ); i++ ) - { - PHB_ITEM pRow = hb_arrayGetItemPtr( pTable, i ); - - for( j = 1; j <= 2; j++ ) - { - USHORT nStrLen = ( USHORT ) hb_arrayGetCLen( pRow, j ) + 1; /* including trailing 0 */ - - HB_PUT_LE_UINT16( buffer, ( UINT16 ) nStrLen ); - - if( hb_fsWrite( handle, ( BYTE * ) buffer, 2 ) != 2 || - hb_fsWrite( handle, ( BYTE * ) hb_arrayGetCPtr( pRow, j ), nStrLen ) != nStrLen ) - return FALSE; - } - } - - return TRUE; + ulLen -= HB_I18N_HEADER_SIZE; + return memcmp( pBuffer, s_signature, HB_I18N_SIG_SIZE ) == 0 && + ( ulLen == 0 || + ( HB_GET_LE_UINT32( &pBuffer[ HB_I18N_SIZE_OFFSET ] ) == ulLen && + HB_GET_LE_UINT32( &pBuffer[ HB_I18N_CRC_OFFSET ] ) == + hb_crc32( 0, ( const BYTE * ) pBuffer + HB_I18N_HEADER_SIZE, + ulLen ) ) ); } -/* Saves a table to disk. aSortedTable must be sorted by original text. - __I18N_SAVE( cFileName | nHandle, aSortedTable ) => lSuccess */ -HB_FUNC( __I18N_SAVE ) +static PHB_I18N_TRANS hb_i18n_deserialize( PHB_ITEM pItem ) { - if( ISCHAR( 1 ) ) - { - HB_FHANDLE handle = hb_fsCreate( ( BYTE * ) hb_parc( 1 ), FC_NORMAL ); + PHB_I18N_TRANS pI18N = NULL; - if( handle != FS_ERROR ) + if( pItem && HB_IS_STRING( pItem ) ) + { + ULONG ulLen = hb_itemGetCLen( pItem ); + const char * pBuffer = hb_itemGetCPtr( pItem ); + + if( ulLen > HB_I18N_HEADER_SIZE && hb_i18n_headercheck( pBuffer, ulLen ) ) { - hb_retl( hb_i18n_save( handle, hb_param( 2, HB_IT_ARRAY ) ) ); - hb_fsClose( handle ); - } - else - hb_retl( FALSE ); - } - else if( ISNUM( 1 ) ) - hb_retl( hb_i18n_save( hb_numToHandle( hb_parnint( 1 ) ), hb_param( 2, HB_IT_ARRAY ) ) ); - else - hb_errRT_BASE_SubstR( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); -} + PHB_ITEM pTable; -static BOOL hb_i18n_memread( BYTE * memory, ULONG memsize, BYTE ** buffer, ULONG nSize, ULONG * offset ) -{ - if( *offset + nSize <= memsize ) - { - *buffer = memory + *offset; - *offset += nSize; - return TRUE; - } - - return FALSE; -} - -static PHB_ITEM hb_i18n_load_from_memory( BYTE * memory, ULONG memsize ) -{ - PHB_ITEM pTable = NULL; - BYTE * buffer; - ULONG offset = 0; - - if( hb_i18n_memread( memory, memsize, &buffer, sizeof( s_szHead ), &offset ) && - memcmp( buffer, s_szHead, sizeof( s_szHead ) ) == 0 && - hb_i18n_memread( memory, memsize, &buffer, 4, &offset ) ) - { - ULONG count = ( ULONG ) HB_GET_LE_UINT32( buffer ); - ULONG i, j; - - pTable = hb_itemArrayNew( count << 1 ); - - for( i = 0; i < count; i++ ) - { - for( j = 1; j <= 2; j++ ) + pBuffer += HB_I18N_HEADER_SIZE; + ulLen -= HB_I18N_HEADER_SIZE; + pTable = hb_itemDeserialize( &pBuffer, &ulLen ); + if( pTable ) { - if( hb_i18n_memread( memory, memsize, &buffer, 2, &offset ) ) - { - ULONG nStrLen = ( ULONG ) HB_GET_LE_UINT16( buffer ); - - if( nStrLen > 0 && hb_i18n_memread( memory, memsize, &buffer, nStrLen, &offset ) && buffer[ nStrLen - 1 ] == '\0' ) - { - hb_arraySetCL( pTable, ( i << 1 ) + j, ( char * ) buffer, nStrLen - 1 ); - continue; - } - } - - hb_itemRelease( pTable ); - pTable = NULL; + pI18N = hb_i18n_initialize( pTable ); + if( !pI18N ) + hb_itemRelease( pTable ); } } } - return pTable; + return pI18N; } -/* Loads a table from memory into a flat array. - __I18N_LOADFROMMEMORY( cBuffer ) => trs */ -HB_FUNC( __I18N_LOADFROMMEMORY ) +static HB_GARBAGE_FUNC( hb_i18n_destructor ) { - if( ISCHAR( 1 ) ) - { - PHB_ITEM pTable = hb_i18n_load_from_memory( ( BYTE * ) hb_parc( 1 ), hb_parclen( 1 ) ); + PHB_I18N_TRANS * pI18NHolder = ( PHB_I18N_TRANS * ) Cargo; - if( pTable ) - hb_itemReturnRelease( pTable ); - else - hb_reta( 0 ); + if( *pI18NHolder ) + { + hb_i18n_release( ( void * ) *pI18NHolder ); + *pI18NHolder = NULL; } - else - hb_errRT_BASE_SubstR( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); } -static PHB_ITEM hb_i18n_load( PHB_FILE file ) +static PHB_I18N_TRANS hb_i18n_param( int * piParam, BOOL fActive ) { - PHB_ITEM pTable = NULL; - BYTE buffer[ 4 ]; - HB_FOFFSET offset = 0; + PHB_I18N_TRANS * pI18NHolder = ( PHB_I18N_TRANS * ) hb_parptrGC( hb_i18n_destructor, *piParam ); - if( hb_fileReadAt( file, buffer, sizeof( s_szHead ), offset ) == sizeof( s_szHead ) && - memcmp( buffer, s_szHead, sizeof( s_szHead ) ) == 0 && - hb_fileReadAt( file, buffer, 4, offset += sizeof( buffer ) ) == 4 ) + if( pI18NHolder ) { - ULONG count = ( ULONG ) HB_GET_LE_UINT32( buffer ); - ULONG i, j; + ( *piParam )++; + return *pI18NHolder; + } - offset += 4; + return fActive ? hb_i18n_table() : NULL; +} - pTable = hb_itemArrayNew( count << 1 ); +static PHB_ITEM hb_i18n_newitem( PHB_I18N_TRANS pI18N ) +{ + PHB_I18N_TRANS * pI18NHolder; + PHB_ITEM pItem = hb_itemNew( NULL ); - for( i = 0; i < count; i++ ) + if( !pI18N ) + pI18N = hb_i18n_new(); + pI18NHolder = ( PHB_I18N_TRANS * ) + hb_gcAlloc( sizeof( PHB_I18N_TRANS ), hb_i18n_destructor ); + *pI18NHolder = pI18N; + + return hb_itemPutPtrGC( pItem, pI18NHolder ); +} + +static BOOL hb_i18n_getpluralform( PHB_I18N_TRANS pI18N, PHB_ITEM pOldForm, + BOOL fBase ) +{ + BOOL fResult = FALSE; + + if( pI18N ) + { + if( pOldForm ) { - for( j = 1; j <= 2; j++ ) + PHB_ITEM pBlock; + int iForm; + + if( fBase ) { - if( hb_fileReadAt( file, buffer, 2, offset ) == 2 ) + pBlock = pI18N->base_plural_block; + iForm = pI18N->base_plural_form; + } + else + { + pBlock = pI18N->plural_block; + iForm = pI18N->plural_form; + } + + if( pBlock ) + hb_itemCopy( pOldForm, pBlock ); + else if( iForm ) + hb_itemPutC( pOldForm, hb_i18n_pluralformid( iForm ) ); + else + hb_itemPutC( pOldForm, "EN" ); /* default is ENGLISH */ + } + fResult = TRUE; + } + return fResult; +} + +static BOOL hb_i18n_setpluralform( PHB_I18N_TRANS pI18N, PHB_ITEM pForm, + BOOL fBase ) +{ + BOOL fResult = FALSE; + + if( pI18N && pForm ) + { + if( HB_IS_BLOCK( pForm ) ) + { + if( fBase ) + { + if( pI18N->base_plural_block ) + hb_itemCopy( pI18N->base_plural_block, pForm ); + else + pI18N->base_plural_block = hb_itemNew( pForm ); + } + else + { + if( pI18N->plural_block ) + hb_itemCopy( pI18N->plural_block, pForm ); + else + pI18N->plural_block = hb_itemNew( pForm ); + } + fResult = TRUE; + } + else if( HB_IS_STRING( pForm ) ) + { + int iForm = hb_i18n_pluralformfind( hb_itemGetCPtr( pForm ) ); + if( iForm ) + { + const char * szKey; + if( fBase ) { - ULONG nStrLen = ( ULONG ) HB_GET_LE_UINT16( buffer ); - - offset += 2; - - if( nStrLen > 0 ) + if( pI18N->base_plural_block ) { - BYTE * string = ( BYTE * ) hb_xgrab( nStrLen ); + hb_itemRelease( pI18N->base_plural_block ); + pI18N->base_plural_block = NULL; + } + pI18N->base_plural_form = iForm; + szKey = "BASE_LANG"; + } + else + { + if( pI18N->plural_block ) + { + hb_itemRelease( pI18N->plural_block ); + pI18N->plural_block = NULL; + } + pI18N->plural_form = iForm; + szKey = "LANG"; + } + hb_i18n_setitem( pI18N->table, szKey, hb_i18n_pluralformid( iForm ) ); + fResult = TRUE; + } + } + } + return fResult; +} - if( hb_fileReadAt( file, string, nStrLen, offset ) == nStrLen && string[ nStrLen - 1 ] == '\0' ) +static const char * hb_i18n_setcodepage( PHB_I18N_TRANS pI18N, + const char * szCdpID, + BOOL fBase, BOOL fTranslate ) +{ + const char * szOldCdpID = NULL, * szKey; + + if( pI18N ) + { + PHB_CODEPAGE cdp = szCdpID ? hb_cdpFind( szCdpID ) : NULL, cdpage; + + cdpage = fBase ? pI18N->base_cdpage : pI18N->cdpage; + if( cdpage ) + szOldCdpID = cdpage->id; + if( cdp && cdp != cdpage ) + { + if( fTranslate && cdpage ) + { + ULONG ulLen = hb_hashLen( pI18N->context_table ), ul; + for( ul = 1; ul <= ulLen; ++ul ) + { + PHB_ITEM pContext = hb_hashGetValueAt( pI18N->context_table, ul ); + PHB_ITEM pResult; + ULONG ulCount = hb_hashLen( pContext ), u; + + for( u = 1; u <= ulCount; ++u ) + { + if( fBase ) { - offset += nStrLen; - hb_arraySetCPtr( pTable, ( i << 1 ) + j, ( char * ) string, nStrLen - 1 ); - continue; + pResult = hb_hashGetKeyAt( pContext, u ); + if( HB_IS_STRING( pResult ) ) + { + pResult = hb_itemUnShareString( pResult ); + hb_cdpnTranslate( hb_itemGetCPtr( pResult ), cdpage, cdp, + hb_itemGetCLen( pResult ) ); + } } else - hb_xfree( string ); + { + pResult = hb_hashGetValueAt( pContext, u ); + if( HB_IS_STRING( pResult ) ) + { + pResult = hb_itemUnShareString( pResult ); + hb_cdpnTranslate( hb_itemGetCPtr( pResult ), cdpage, cdp, + hb_itemGetCLen( pResult ) ); + } + else if( HB_IS_ARRAY( pResult ) ) + { + ULONG ulTrans = hb_arrayLen( pResult ), u2; + for( u2 = 1; u2 <= ulTrans; ++u2 ) + { + PHB_ITEM pValue = hb_arrayGetItemPtr( pResult, u2 ); + if( HB_IS_STRING( pValue ) ) + { + pValue = hb_itemUnShareString( pValue ); + hb_cdpnTranslate( hb_itemGetCPtr( pValue ), cdpage, cdp, + hb_itemGetCLen( pValue ) ); + } + } + } + } + } + if( fBase ) + { + pResult = hb_hashGetKeyAt( pI18N->context_table, ul ); + if( HB_IS_STRING( pResult ) ) + { + pResult = hb_itemUnShareString( pResult ); + hb_cdpnTranslate( hb_itemGetCPtr( pResult ), cdpage, cdp, + hb_itemGetCLen( pResult ) ); + } + hb_hashSetFlags( pContext, HB_HASH_RESORT ); } } - - hb_itemRelease( pTable ); - pTable = NULL; + if( fBase ) + hb_hashSetFlags( pI18N->context_table, HB_HASH_RESORT ); } - } - } - return pTable; -} - -/* Loads a table in a flat array. - __I18N_LOAD( cFileName ) => trs */ -HB_FUNC( __I18N_LOAD ) -{ - if( ISCHAR( 1 ) ) - { - PHB_ITEM pTable = NULL; - PHB_FILE file = hb_fileExtOpen( ( BYTE * ) hb_parc( 1 ), NULL, FO_READ | FO_DENYNONE, NULL, NULL ); - - if( file ) - { - pTable = hb_i18n_load( file ); - hb_fileClose( file ); - } - - if( pTable ) - hb_itemReturnRelease( pTable ); - else - hb_reta( 0 ); - } - else - hb_errRT_BASE_SubstR( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); -} - -/* Translate an Harbour string or return it untranslated - __I18N_GETTEXT( @cText, trs ) => NIL */ -HB_FUNC( __I18N_GETTEXT ) -{ - PHB_ITEM pFind = hb_param( 1, HB_IT_STRING ); - - if( pFind ) - { - PHB_ITEM pTable = hb_param( 2, HB_IT_ARRAY ); - - if( pTable ) - { - char * pszFind = hb_itemGetCPtr( pFind ); - - ULONG nLow = 1; - ULONG nHigh = ( hb_arrayLen( pTable ) >> 1 ); - ULONG nMiddle; - - while( nLow <= nHigh ) + if( fBase ) { - int result = strcmp( hb_arrayGetCPtr( pTable, ( ( nMiddle = ( nLow + nHigh ) / 2 ) << 1 ) - 1 ), pszFind ); - - if( result == 0 ) - { - hb_itemParamStore( 1, hb_arrayGetItemPtr( pTable, nMiddle << 1 ) ); - break; - } - else if( result > 0 ) - nHigh = nMiddle - 1; + pI18N->base_cdpage = cdp; + szKey = "BASE_CODEPAGE"; + } + else + { + pI18N->cdpage = cdp; + szKey = "CODEPAGE"; + } + hb_i18n_setitem( pI18N->table, szKey, szCdpID ); + } + } + + return szOldCdpID; +} + +static const char * hb_i18n_description( PHB_I18N_TRANS pI18N, PHB_ITEM pItem ) +{ + if( pI18N ) + { + PHB_ITEM pKey = hb_itemPutC( NULL, "DESCRIPTION" ), pValue; + + pValue = hb_hashGetItemPtr( pI18N->table, pKey, 0 ); + if( pItem ) + { + if( HB_IS_STRING( pItem ) ) + { + if( pValue ) + hb_itemCopy( pValue, pItem ); else - nLow = nMiddle + 1; + { + hb_hashAdd( pI18N->table, pKey, pItem ); + pValue = hb_hashGetItemPtr( pI18N->table, pKey, 0 ); + } } } + hb_itemRelease( pKey ); + + return hb_itemGetCPtr( pValue ); + } + + return NULL; +} + +static void hb_i18n_addtext( PHB_I18N_TRANS pI18N, PHB_ITEM pMsgID, + PHB_ITEM pTrans, PHB_ITEM pContext ) +{ + PHB_ITEM pTable = pContext ? hb_hashGetItemPtr( pI18N->context_table, + pContext, 0 ) : pI18N->default_context; + if( !pTable ) + { + pTable = hb_hashNew( hb_itemNew( NULL ) ); + hb_hashAdd( pTable, pMsgID, pTrans ); + hb_hashAdd( pI18N->context_table, pContext, pTable ); + hb_itemRelease( pTable ); + } + else + hb_hashAdd( pTable, pMsgID, pTrans ); +} + +PHB_ITEM hb_i18n_gettext( PHB_ITEM pMsgID, PHB_ITEM pContext ) +{ + PHB_I18N_TRANS pI18N = hb_i18n_table(); + PHB_CODEPAGE cdpage = NULL; + + if( pI18N ) + { + PHB_ITEM pTable = pContext && pI18N->context_table ? + hb_hashGetItemPtr( pI18N->context_table, pContext, 0 ) : + pI18N->default_context; + + cdpage = pI18N->base_cdpage; + if( pTable ) + { + pTable = hb_hashGetItemPtr( pTable, pMsgID, 0 ); + if( pTable ) + { + if( HB_IS_ARRAY( pTable ) ) + pTable = hb_arrayGetItemPtr( pTable, 1 ); + if( pTable && HB_IS_STRING( pTable ) ) + { + pMsgID = pTable; + cdpage = pI18N->cdpage; + } + } + } + } + + if( pMsgID ) + { + if( HB_IS_STRING( pMsgID ) ) + { + if( cdpage ) + { + PHB_CODEPAGE cdp = hb_vmCDP(); + if( cdp && cdp != cdpage ) + { + pMsgID = hb_itemUnShareString( pMsgID ); + hb_cdpnTranslate( hb_itemGetCPtr( pMsgID ), cdpage, cdp, + hb_itemGetCLen( pMsgID ) ); + } + } + } + else + pMsgID = NULL; + } + + return pMsgID; +} + +PHB_ITEM hb_i18n_ngettext( PHB_ITEM pNum, PHB_ITEM pMsgID, PHB_ITEM pContext ) +{ + PHB_I18N_TRANS pI18N = hb_i18n_table(); + PHB_CODEPAGE cdpage = NULL; + PHB_ITEM pBlock = NULL; + int iPluralForm = 0; + + if( pI18N ) + { + PHB_ITEM pTable = pContext && pI18N->context_table ? + hb_hashGetItemPtr( pI18N->context_table, pContext, 0 ) : + pI18N->default_context; + + cdpage = pI18N->base_cdpage; + pBlock = pI18N->base_plural_block; + iPluralForm = pI18N->base_plural_form; + + if( pTable ) + { + PHB_ITEM pMsg = HB_IS_ARRAY( pMsgID ) ? + hb_arrayGetItemPtr( pMsgID, 1 ) : pMsgID; + pTable = pMsg && HB_IS_STRING( pMsg ) ? + hb_hashGetItemPtr( pTable, pMsg, 0 ) : NULL; + if( pTable ) + { + if( HB_IS_STRING( pTable ) || + ( HB_IS_ARRAY( pTable ) && + ( hb_arrayGetType( pTable, 1 ) & HB_IT_STRING ) != 0 ) ) + { + pMsgID = pTable; + cdpage = pI18N->cdpage; + pBlock = pI18N->plural_block; + iPluralForm = pI18N->plural_form; + } + } + } + } + + if( HB_IS_ARRAY( pMsgID ) ) + { + long lIndex; + + if( !pNum ) + lIndex = 1; + else if( pBlock ) + { + hb_evalBlock1( pBlock, pNum ); + lIndex = hb_parnl( -1 ); + } + else + lIndex = hb_i18n_pluralindex( iPluralForm, pNum ); + + if( lIndex < 1 || ( lIndex != 1 && + ( hb_arrayGetType( pMsgID, lIndex ) & HB_IT_STRING ) == 0 ) ) + lIndex = 1; + + pMsgID = hb_arrayGetItemPtr( pMsgID, lIndex ); + } + + if( pMsgID ) + { + if( HB_IS_STRING( pMsgID ) ) + { + if( cdpage ) + { + PHB_CODEPAGE cdp = hb_vmCDP(); + if( cdp && cdp != cdpage ) + { + pMsgID = hb_itemUnShareString( pMsgID ); + hb_cdpnTranslate( hb_itemGetCPtr( pMsgID ), cdpage, cdp, + hb_itemGetCLen( pMsgID ) ); + } + } + } + else + pMsgID = NULL; + } + + return pMsgID; +} + + +/* + * base .prg i18n functions + */ + +HB_FUNC( HB_I18N_GETTEXT ) +{ + PHB_ITEM pMsgID = hb_param( 1, HB_IT_STRING ); + PHB_ITEM pContext = hb_param( 2, HB_IT_STRING ); + + if( pMsgID ) + pMsgID = hb_i18n_gettext( pMsgID, pContext ); + + if( pMsgID && HB_IS_STRING( pMsgID ) ) + hb_itemReturn( pMsgID ); + else + hb_errRT_BASE_SubstR( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +} + +HB_FUNC( HB_I18N_NGETTEXT ) +{ + PHB_ITEM pNum = hb_param( 1, HB_IT_NUMERIC ); + PHB_ITEM pMsgID = hb_param( 2, HB_IT_STRING | HB_IT_ARRAY ); + PHB_ITEM pContext = hb_param( 3, HB_IT_STRING ); + + if( !pNum ) + pMsgID = NULL; + else if( pMsgID ) + pMsgID = hb_i18n_ngettext( pNum, pMsgID, pContext ); + + if( pMsgID && HB_IS_STRING( pMsgID ) ) + hb_itemReturn( pMsgID ); + else + hb_errRT_BASE_SubstR( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +} + +HB_FUNC( HB_I18N_GETTEXT_STRICT ) +{ + HB_FUNC_EXEC( HB_I18N_GETTEXT ); +} + +HB_FUNC( HB_I18N_NGETTEXT_STRICT ) +{ + HB_FUNC_EXEC( HB_I18N_NGETTEXT ); +} + + +/* + * extended .prg i18n functions to create and manage translation tables + */ + +HB_FUNC( HB_I18N_CREATE ) +{ + hb_itemReturnRelease( hb_i18n_newitem( NULL ) ); +} + +HB_FUNC( HB_I18N_CODEPAGE ) +{ + PHB_I18N_TRANS pI18N; + int iParam = 1; + + pI18N = hb_i18n_param( &iParam, TRUE ); + + if( pI18N ) + hb_retc( hb_i18n_setcodepage( pI18N, hb_parc( iParam ), + ISLOG( iParam + 1 ) && hb_parl( iParam + 1 ), + ISLOG( iParam + 2 ) && hb_parl( iParam + 2 ) ) ); + else + hb_errRT_BASE_SubstR( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); + +} + +HB_FUNC( HB_I18N_PLURALFORM ) +{ + PHB_I18N_TRANS pI18N; + int iParam = 1; + + pI18N = hb_i18n_param( &iParam, TRUE ); + if( pI18N ) + { + PHB_ITEM pOldForm = hb_itemNew( NULL ); + PHB_ITEM pForm = hb_param( iParam, HB_IT_STRING | HB_IT_BLOCK ); + BOOL fBase = ISLOG( iParam + 1 ) && hb_parl( iParam + 1 ); + + if( hb_i18n_getpluralform( pI18N, pOldForm, fBase ) ) + hb_itemReturn( pOldForm ); + hb_itemRelease( pOldForm ); + if( pForm ) + hb_i18n_setpluralform( pI18N, pForm, fBase ); } else hb_errRT_BASE_SubstR( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); } + +HB_FUNC( HB_I18N_DESCRIPTION ) +{ + PHB_I18N_TRANS pI18N; + int iParam = 1; + + pI18N = hb_i18n_param( &iParam, TRUE ); + if( pI18N ) + { + PHB_ITEM pNewDescript = hb_param( iParam, HB_IT_STRING ); + + hb_retc( hb_i18n_description( pI18N, NULL ) ); + if( pNewDescript ) + hb_i18n_description( pI18N, pNewDescript ); + } + else + hb_errRT_BASE_SubstR( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +} + +HB_FUNC( HB_I18N_ADDTEXT ) +{ + PHB_I18N_TRANS pI18N; + int iParam = 1; + + pI18N = hb_i18n_param( &iParam, FALSE ); + if( pI18N ) + { + PHB_ITEM pMsgID = hb_param( iParam, HB_IT_STRING ); + PHB_ITEM pTrans = hb_param( iParam + 1, HB_IT_STRING | HB_IT_ARRAY ); + PHB_ITEM pContext = hb_param( iParam + 2, HB_IT_STRING ); + + if( pMsgID && pTrans ) + { + if( HB_IS_ARRAY( pTrans ) ) + { + ULONG ulLen = hb_arrayLen( pTrans ), ul; + if( ulLen != 0 ) + { + for( ul = 1; ul <= ulLen; ++ul ) + { + if( !HB_IS_STRING( hb_arrayGetItemPtr( pTrans, ul ) ) ) + { + pTrans = NULL; + break; + } + } + } + else + pTrans = NULL; + } + + if( pTrans ) + { + hb_i18n_addtext( pI18N, pMsgID, pTrans, pContext ); + return; + } + } + } + + hb_errRT_BASE_SubstR( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +} + +HB_FUNC( HB_I18N_SET ) +{ + + if( hb_pcount() > 0 ) + { + if( ISNIL( 1 ) ) + hb_vmSetI18N( NULL ); + else + { + int iParam = 1; + PHB_I18N_TRANS pI18N = hb_i18n_param( &iParam, FALSE ); + if( pI18N ) + hb_vmSetI18N( hb_i18n_alloc( pI18N ) ); + else + { + hb_errRT_BASE_SubstR( EG_ARG, 3012, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); + return; + } + } + } + hb_retl( hb_i18n_table() != NULL ); +} + +HB_FUNC( HB_I18N_SAVETABLE ) +{ + PHB_I18N_TRANS pI18N; + int iParam = 1; + + pI18N = hb_i18n_param( &iParam, TRUE ); + if( pI18N ) + hb_itemReturnRelease( hb_i18n_serialize( pI18N ) ); +} + +HB_FUNC( HB_I18N_RESTORETABLE ) +{ + PHB_ITEM pItem = hb_param( 1, HB_IT_STRING ); + + if( pItem ) + { + PHB_I18N_TRANS pI18N = hb_i18n_deserialize( pItem ); + if( pI18N ) + hb_itemReturnRelease( hb_i18n_newitem( pI18N ) ); + } +} + +HB_FUNC( HB_I18N_HEADERSIZE ) +{ + hb_retni( HB_I18N_HEADER_SIZE ); +} + +HB_FUNC( HB_I18N_CHECK ) +{ + hb_retl( hb_i18n_headercheck( hb_parc( 1 ), hb_parclen( 1 ) ) ); +} + +/* unoffical function to access ineternal hash table used by i18n set */ +HB_FUNC( __I18N_HASHTABLE ) +{ + PHB_I18N_TRANS pI18N; + + if( ISHASH( 1 ) ) + { + pI18N = hb_i18n_initialize( hb_param( 1, HB_IT_HASH ) ); + if( pI18N ) + hb_itemReturnRelease( hb_i18n_newitem( pI18N ) ); + } + else + { + int iParam = 1; + + pI18N = hb_i18n_param( &iParam, TRUE ); + if( pI18N ) + hb_itemReturn( pI18N->table ); + } +} diff --git a/harbour/source/rtl/itemseri.c b/harbour/source/rtl/itemseri.c index cf54b62554..d3a29ed59d 100644 --- a/harbour/source/rtl/itemseri.c +++ b/harbour/source/rtl/itemseri.c @@ -153,7 +153,7 @@ typedef struct _HB_CYCLIC_REF struct _HB_CYCLIC_REF * pNext; } HB_CYCLIC_REF, * PHB_CYCLIC_REF; -static ULONG hb_deserializeItem( PHB_ITEM pItem, UCHAR * pBuffer, +static ULONG hb_deserializeItem( PHB_ITEM pItem, const UCHAR * pBuffer, ULONG ulOffset, PHB_CYCLIC_REF pRef ); static BOOL hb_itemSerialValueRef( PHB_CYCLIC_REF * pRefPtr, void * value, @@ -276,6 +276,9 @@ static ULONG hb_itemSerialSize( PHB_ITEM pItem, BOOL fNumSize, PHB_CYCLIC_REF * USHORT uiClass; const char * szVal; + if( HB_IS_BYREF( pItem ) ) + pItem = hb_itemUnRef( pItem ); + switch( hb_itemType( pItem ) ) { case HB_IT_NIL: @@ -409,6 +412,9 @@ static ULONG hb_serializeItem( PHB_ITEM pItem, BOOL fNumSize, UCHAR * pBuffer, const char * szVal; ULONG ulRef, ulLen, u; + if( HB_IS_BYREF( pItem ) ) + pItem = hb_itemUnRef( pItem ); + switch( hb_itemType( pItem ) ) { case HB_IT_NIL: @@ -701,7 +707,8 @@ static ULONG hb_serializeItem( PHB_ITEM pItem, BOOL fNumSize, UCHAR * pBuffer, return ulOffset; } -static ULONG hb_deserializeHash( PHB_ITEM pItem, UCHAR * pBuffer, ULONG ulOffset, +static ULONG hb_deserializeHash( PHB_ITEM pItem, + const UCHAR * pBuffer, ULONG ulOffset, ULONG ulLen, PHB_CYCLIC_REF pRef ) { hb_hashNew( pItem ); @@ -740,7 +747,8 @@ static ULONG hb_deserializeHash( PHB_ITEM pItem, UCHAR * pBuffer, ULONG ulOffset return ulOffset; } -static ULONG hb_deserializeArray( PHB_ITEM pItem, UCHAR * pBuffer, ULONG ulOffset, +static ULONG hb_deserializeArray( PHB_ITEM pItem, + const UCHAR * pBuffer, ULONG ulOffset, ULONG ulLen, PHB_CYCLIC_REF pRef ) { ULONG u; @@ -753,7 +761,7 @@ static ULONG hb_deserializeArray( PHB_ITEM pItem, UCHAR * pBuffer, ULONG ulOffse return ulOffset; } -static ULONG hb_deserializeItem( PHB_ITEM pItem, UCHAR * pBuffer, +static ULONG hb_deserializeItem( PHB_ITEM pItem, const UCHAR * pBuffer, ULONG ulOffset, PHB_CYCLIC_REF pRef ) { ULONG ulLen, ulPad; @@ -953,8 +961,8 @@ static ULONG hb_deserializeItem( PHB_ITEM pItem, UCHAR * pBuffer, case HB_SERIAL_OBJ: { - char * szClass, * szFunc; - szClass = ( char * ) &pBuffer[ ulOffset ]; + const char * szClass, * szFunc; + szClass = ( const char * ) &pBuffer[ ulOffset ]; ulLen = strlen( szClass ); szFunc = szClass + ulLen + 1; ulOffset = hb_deserializeItem( pItem, pBuffer, @@ -971,10 +979,10 @@ static ULONG hb_deserializeItem( PHB_ITEM pItem, UCHAR * pBuffer, return ulOffset; } -static BOOL hb_deserializeTest( UCHAR ** pBufferPtr, ULONG * pulSize, +static BOOL hb_deserializeTest( const UCHAR ** pBufferPtr, ULONG * pulSize, ULONG ulOffset, PHB_CYCLIC_REF * pRefPtr ) { - UCHAR * pBuffer = * pBufferPtr; + const UCHAR * pBuffer = * pBufferPtr; ULONG ulSize = * pulSize, ulLen = 0; if( ulSize == 0 ) @@ -1153,9 +1161,9 @@ static BOOL hb_deserializeTest( UCHAR ** pBufferPtr, ULONG * pulSize, } /* - * These function will be public in the future [druzus] + * public API functions */ -static char * hb_itemSerial( PHB_ITEM pItem, BOOL fNumSize, ULONG *pulSize ) +char * hb_itemSerialize( PHB_ITEM pItem, BOOL fNumSize, ULONG *pulSize ) { PHB_CYCLIC_REF pRef = NULL; ULONG ulSize = hb_itemSerialSize( pItem, fNumSize, &pRef, 0 ); @@ -1172,16 +1180,13 @@ static char * hb_itemSerial( PHB_ITEM pItem, BOOL fNumSize, ULONG *pulSize ) return ( char * ) pBuffer; } -/* - * These function will be public in the future [druzus] - */ -static PHB_ITEM hb_itemDeserial( char ** pBufferPtr, ULONG * pulSize ) +PHB_ITEM hb_itemDeserialize( const char ** pBufferPtr, ULONG * pulSize ) { PHB_CYCLIC_REF pRef = NULL; - UCHAR * pBuffer = ( UCHAR * ) *pBufferPtr; + const UCHAR * pBuffer = ( const UCHAR * ) *pBufferPtr; PHB_ITEM pItem = NULL; - if( !pulSize || hb_deserializeTest( ( UCHAR ** ) pBufferPtr, pulSize, 0, &pRef ) ) + if( !pulSize || hb_deserializeTest( ( const UCHAR ** ) pBufferPtr, pulSize, 0, &pRef ) ) { pItem = hb_itemNew( NULL ); hb_deserializeItem( pItem, pBuffer, 0, pRef ); @@ -1198,7 +1203,7 @@ HB_FUNC( HB_SERIALIZE ) if( pItem ) { ULONG ulSize; - char * pBuffer = hb_itemSerial( pItem, ISLOG( 2 ) && hb_parl( 2 ), &ulSize ); + char * pBuffer = hb_itemSerialize( pItem, ISLOG( 2 ) && hb_parl( 2 ), &ulSize ); hb_retclen_buffer( pBuffer, ulSize ); } } @@ -1210,9 +1215,9 @@ HB_FUNC( HB_DESERIALIZE ) if( ulSize ) { - char * pBuffer = hb_parc( 1 ); + const char * pBuffer = hb_parc( 1 ); - pItem = hb_itemDeserial( &pBuffer, &ulSize ); + pItem = hb_itemDeserialize( &pBuffer, &ulSize ); if( pItem ) { hb_itemReturn( pItem ); diff --git a/harbour/source/vm/estack.c b/harbour/source/vm/estack.c index 20319fe36c..acd08cfacd 100644 --- a/harbour/source/vm/estack.c +++ b/harbour/source/vm/estack.c @@ -945,6 +945,20 @@ void hb_stackSetLang( void * pLang ) hb_stack.pLang = pLang; } +#undef hb_stackGetI18N +void * hb_stackGetI18N( void ) +{ + HB_STACK_TLS_PRELOAD + return hb_stack.pI18N; +} + +#undef hb_stackSetI18N +void hb_stackSetI18N( void * pI18N ) +{ + HB_STACK_TLS_PRELOAD + hb_stack.pI18N = pI18N; +} + #undef hb_stackItemBasePtr PHB_ITEM ** hb_stackItemBasePtr( void ) { diff --git a/harbour/source/vm/hvm.c b/harbour/source/vm/hvm.c index c42c1bd99f..2d0f904533 100644 --- a/harbour/source/vm/hvm.c +++ b/harbour/source/vm/hvm.c @@ -752,6 +752,9 @@ void hb_vmThreadInit( void * Cargo ) hb_cdpSelectID( pState->pszCDP ); hb_langSelectID( pState->pszLang ); + hb_vmSetI18N( pState->pI18N ); + pState->pI18N = NULL; + if( pState->pSet ) { /* TODO: add set sharing */ @@ -814,6 +817,7 @@ void hb_vmThreadQuit( void ) hb_rddCloseAll(); /* close all workareas */ hb_stackRemove( 1 ); /* clear stack items, leave only initial symbol item */ hb_memvarsClear(); /* clear all PUBLIC (and PRIVATE if any) variables */ + hb_vmSetI18N( NULL ); /* remove i18n translation table */ #ifndef HB_NO_DEBUG hb_vmDebuggerExit( FALSE ); /* deactivate debugger */ #endif @@ -885,6 +889,8 @@ void hb_vmInit( BOOL bStartMainProc ) s_VMFlags = hb_cmdargProcessVM( &s_VMCancelKey, &s_VMCancelKeyEx ); hb_inkeySetCancelKeys( s_VMCancelKey, s_VMCancelKeyEx ); + hb_i18n_init(); /* initialize i18n module */ + #ifndef HB_NO_PROFILER /* Initialize opcodes profiler support arrays */ { @@ -1021,8 +1027,6 @@ int hb_vmQuit( void ) hb_itemClear( hb_stackReturnItem() ); hb_stackRemove( 1 ); /* clear stack items, leave only initial symbol item */ - hb_memvarsClear(); /* clear all PUBLIC (and PRIVATE if any) variables */ - /* intentionally here to allow executing object destructors for all * cross referenced items before we release classy subsystem */ @@ -1034,7 +1038,12 @@ int hb_vmQuit( void ) hb_stackSetActionRequest( 0 ); hb_rddCloseAll(); /* close all workareas */ hb_rddShutDown(); /* remove all registered RDD drivers */ + hb_memvarsClear(); /* clear all PUBLIC (and PRIVATE if any) variables */ + hb_vmSetI18N( NULL ); /* remove i18n translation table */ + hb_i18n_exit(); /* unregister i18n module */ + hb_itemClear( hb_stackReturnItem() ); + hb_gcCollectAll( TRUE ); #ifndef HB_NO_DEBUG /* deactivate debugger */ hb_vmDebuggerExit( TRUE ); @@ -8020,6 +8029,21 @@ void hb_vmSetLang( PHB_LANG pLang ) hb_stackSetLang( ( void * ) pLang ); } +void * hb_vmI18N( void ) +{ + HB_STACK_TLS_PRELOAD + + return hb_stackGetI18N(); +} + +void hb_vmSetI18N( void * pI18N ) +{ + HB_STACK_TLS_PRELOAD + + hb_i18n_release( hb_stackGetI18N() ); + hb_stackSetI18N( pI18N ); +} + #if defined( HB_MT_VM ) # define HB_XVM_RETURN \ if( hb_vmThreadRequest ) \ diff --git a/harbour/source/vm/macro.c b/harbour/source/vm/macro.c index 426803da16..cb5f8187f1 100644 --- a/harbour/source/vm/macro.c +++ b/harbour/source/vm/macro.c @@ -939,7 +939,7 @@ char * hb_macroGetType( HB_ITEM_PTR pItem ) if( struMacro.pError ) { ULONG ulGenCode; - + ulGenCode = hb_errGetGenCode( struMacro.pError ); if( ulGenCode == EG_NOVAR || ulGenCode == EG_NOALIAS ) { diff --git a/harbour/source/vm/thread.c b/harbour/source/vm/thread.c index 2e4be61060..73194c4e22 100644 --- a/harbour/source/vm/thread.c +++ b/harbour/source/vm/thread.c @@ -59,6 +59,7 @@ #include "hbvmopt.h" #include "hbthread.h" +#include "hbatomic.h" #include "hbapiitm.h" #include "hbapierr.h" #include "hbapicdp.h" @@ -196,6 +197,87 @@ ULONG _hb_gettid( void ) } #endif +/* + * atomic increment/decrement operations + */ +#if !defined( HB_MT_VM ) +void hb_atomic_set( volatile HB_COUNTER * pCounter, HB_COUNTER value ) +{ + *pCounter = value; +} + +HB_COUNTER hb_atomic_get( volatile HB_COUNTER * pCounter ) +{ + return *pCounter; +} + +void hb_atomic_inc( volatile HB_COUNTER * pCounter ) +{ + ++( *pCounter ); +} + +BOOL hb_atomic_dec( volatile HB_COUNTER * pCounter ) +{ + return --( *pCounter ) == 0; +} +#elif defined( HB_ATOM_INC ) && defined( HB_ATOM_DEC ) && \ + defined( HB_ATOM_GET ) && defined( HB_ATOM_SET ) +void hb_atomic_set( volatile HB_COUNTER * pCounter, HB_COUNTER value ) +{ + HB_ATOM_SET( pCounter, value ); +} + +HB_COUNTER hb_atomic_get( volatile HB_COUNTER * pCounter ) +{ + return HB_ATOM_GET( pCounter ); +} + +void hb_atomic_inc( volatile HB_COUNTER * pCounter ) +{ + HB_ATOM_INC( pCounter ); +} + +BOOL hb_atomic_dec( volatile HB_COUNTER * pCounter ) +{ + return HB_ATOM_DEC( pCounter ) == 0; +} +#else +static HB_CRITICAL_NEW( s_atomicMtx ); +void hb_atomic_set( volatile HB_COUNTER * pCounter, HB_COUNTER value ) +{ + /* NOTE: on some platforms it may be necessary to protect this + * by cirtical section, f.e. when HB_COUNTER cannot be accessed + * using single memory access by CPU. + */ + *pCounter = value; +} + +HB_COUNTER hb_atomic_get( volatile HB_COUNTER * pCounter ) +{ + /* NOTE: on some platforms it may be necessary to protect this + * by cirtical section, f.e. when HB_COUNTER cannot be accessed + * using single memory access by CPU. + */ + return *pCounter; +} + +void hb_atomic_inc( volatile HB_COUNTER * pCounter ) +{ + hb_threadEnterCriticalSection( &s_atomicMtx ); + ++( *pCounter ); + hb_threadLeaveCriticalSection( &s_atomicMtx ); +} + +BOOL hb_atomic_dec( volatile HB_COUNTER * pCounter ) +{ + BOOL fResult; + hb_threadEnterCriticalSection( &s_atomicMtx ); + fResult = --( *pCounter ) == 0; + hb_threadLeaveCriticalSection( &s_atomicMtx ); + return fResult; +} +#endif + void hb_threadEnterCriticalSection( HB_CRITICAL_T * critical ) { #if !defined( HB_MT_VM ) @@ -480,6 +562,11 @@ static HB_GARBAGE_FUNC( hb_threadDestructor ) hb_itemRelease( pThread->pResult ); pThread->pResult = NULL; } + if( pThread->pI18N ) + { + hb_i18n_release( pThread->pI18N ); + pThread->pI18N = NULL; + } if( pThread->pSet ) { hb_setRelease( pThread->pSet ); @@ -657,6 +744,7 @@ HB_FUNC( HB_THREADSTART ) pThread->pszCDP = hb_cdpID(); pThread->pszLang = hb_langID(); + pThread->pI18N = hb_i18n_alloc( hb_vmI18N() ); pThread->pszDefRDD = hb_stackRDD()->szDefaultRDD; pThread->pSet = hb_setClone( hb_stackSetStruct() ); pThread->pParams = hb_arrayBaseParams();