diff options
| author | Stanislav Malyshev <stas@php.net> | 2008-07-07 22:51:04 +0000 |
|---|---|---|
| committer | Stanislav Malyshev <stas@php.net> | 2008-07-07 22:51:04 +0000 |
| commit | 0d16b1516b6b9ef0c2696bc19069e4cda5aee0ea (patch) | |
| tree | df4e6a46dea0afafdbc912919b32c2806841a4eb /ext/intl/locale/locale_methods.c | |
| parent | 3bab7c18ac205863af3df740144be23c18cf7a72 (diff) | |
| download | php-git-0d16b1516b6b9ef0c2696bc19069e4cda5aee0ea.tar.gz | |
Merge intl extension into core
Diffstat (limited to 'ext/intl/locale/locale_methods.c')
| -rwxr-xr-x | ext/intl/locale/locale_methods.c | 1732 |
1 files changed, 1732 insertions, 0 deletions
diff --git a/ext/intl/locale/locale_methods.c b/ext/intl/locale/locale_methods.c new file mode 100755 index 0000000000..1f6ca4d182 --- /dev/null +++ b/ext/intl/locale/locale_methods.c @@ -0,0 +1,1732 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Kirti Velankar <kirtig@yahoo-inc.com> | + +----------------------------------------------------------------------+ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <unicode/ustring.h> +#include <unicode/udata.h> +#include <unicode/putil.h> + +#include "php_intl.h" +#include "locale.h" +#include "locale_class.h" +#include "locale_methods.h" +#include "intl_convert.h" + +#include <zend_API.h> +#include <zend.h> +#include <php.h> +#include "main/php_ini.h" + +ZEND_EXTERN_MODULE_GLOBALS( intl ) + +//Sizes required for the strings "variant15" , "extlang11", "private12" etc. +#define SEPARATOR "_" +#define SEPARATOR1 "-" +#define DELIMITER "-_" +#define EXTLANG_PREFIX "a" +#define PRIVATE_PREFIX "x" +#define DISP_NAME "name" + +#define MAX_NO_VARIANT 15 +#define MAX_NO_EXTLANG 3 +#define MAX_NO_PRIVATE 15 +#define MAX_NO_LOOKUP_LANG_TAG 100 + +//Sizes required for the strings "variant15" , "extlang3", "private12" etc. +#define VARIANT_KEYNAME_LEN 11 +#define EXTLANG_KEYNAME_LEN 10 +#define PRIVATE_KEYNAME_LEN 11 + +/* Based on IANA registry at the time of writing this code +* +*/ +static const char * const LOC_GRANDFATHERED[] = { + "art-lojban", "i-klingon", "i-lux", "i-navajo", "no-bok", "no-nyn", + "cel-gaulish", "en-GB-oed", "i-ami", + "i-bnn", "i-default", "i-enochian", + "i-mingo", "i-pwn", "i-tao", + "i-tay", "i-tsu", "sgn-BE-fr", + "sgn-BE-nl", "sgn-CH-de", "zh-cmn", + "zh-cmn-Hans", "zh-cmn-Hant", "zh-gan" , + "zh-guoyu", "zh-hakka", "zh-min", + "zh-min-nan", "zh-wuu", "zh-xiang", + "zh-yue", NULL +}; + +/* Based on IANA registry at the time of writing this code +* This array lists the preferred values for the grandfathered tags if applicable +* This is in sync with the array LOC_GRANDFATHERED +* e.g. the offsets of the grandfathered tags match the offset of the preferred value +*/ +static const int LOC_PREFERRED_GRANDFATHERED_LEN = 6; +static const char * const LOC_PREFERRED_GRANDFATHERED[] = { + "jbo", "tlh", "lb", + "nv", "nb", "nn", + NULL +}; + +/*returns TRUE if a is an ID separator FALSE otherwise*/ +#define isIDSeparator(a) (a == '_' || a == '-') +#define isKeywordSeparator(a) (a == '@' ) +#define isEndOfTag(a) (a == '\0' ) + +#define isPrefixLetter(a) ((a=='x')||(a=='X')||(a=='i')||(a=='I')) + +/*returns TRUE if one of the special prefixes is here (s=string) + 'x-' or 'i-' */ +#define isIDPrefix(s) (isPrefixLetter(s[0])&&isIDSeparator(s[1])) +#define isKeywordPrefix(s) ( isKeywordSeparator(s[0]) ) + +/* Dot terminates it because of POSIX form where dot precedes the codepage + * except for variant + */ +#define isTerminator(a) ((a==0)||(a=='.')||(a=='@')) + + +/*{{{ return the offset of 'key' in the array 'list'. + * returns -1 if not present + */ +static int16_t findOffset(const char* const* list, const char* key) +{ + const char* const* anchor = list; + while (*list != NULL) { + if (strcmp(key, *list) == 0) { + return (int16_t)(list - anchor); + } + list++; + } + + return -1; + +} +/*}}}*/ + +static char* getPreferredTag(char* gf_tag) +{ + char* result = NULL; + int grOffset = 0; + + grOffset = findOffset( LOC_GRANDFATHERED ,gf_tag); + if( grOffset < LOC_PREFERRED_GRANDFATHERED_LEN ){ + //return preferred tag + result = estrdup( LOC_PREFERRED_GRANDFATHERED[grOffset] ); + } else { + //Return correct grandfathered language tag + result = estrdup( LOC_GRANDFATHERED[grOffset] ); + } + return result; +} + +/* {{{ +* returns the position of next token for lookup +* or -1 if no token +* strtokr equivalent search for token in reverse direction +*/ +static int getStrrtokenPos(char* str, int savedPos) +{ + int result =-1; + int i=0; + + for( i=savedPos; i>=0 ;i--){ + if( isIDSeparator(*(str+i)) ){ + //delimiter found; check for singleton + if( isIDSeparator(*(str+i-2)) ){ + //a singleton; so send the position of token before the singleton + result = i-3; + } else { + result = i-1; + } + break; + } + } + if(result < 1){ + //Just in case inavlid locale e.g. '-x-xyz' or '-sl_Latn' + result =-1; + } + return result; +} +/* }}} */ + +/* {{{ +* returns the position of a singleton if present +* returns -1 if no singleton +* strtok equivalent search for singleton +*/ +static int getSingletonPos(char* str) +{ + int result =-1; + int i=0; + int len = 0; + + if( str && ((len=strlen(str))>0) ){ + for( i=0; i<len ; i++){ + if( isIDSeparator(*(str+i)) ){ + if( i==1){ + // string is of the form x-avy or a-prv1 + result =0; + break; + } else { + //delimiter found; check for singleton + if( isIDSeparator(*(str+i+2)) ){ + //a singleton; so send the position of separator before singleton + result = i+1; + break; + } + } + } + }//end of for + + } + return result; +} +/* }}} */ + +/* {{{ proto static string Locale::getDefault( ) + * Gets the default locale + }}} */ +/* {{{ proto static string locale_get_default( ) + * Gets the default locale + */ +PHP_NAMED_FUNCTION( zif_locale_get_default){ + if( INTL_G(default_locale) == NULL ) { + INTL_G(default_locale) = pestrdup( uloc_getDefault(), 1); + } + RETURN_STRING( INTL_G(default_locale), TRUE ); +} + +/* }}} */ + +/* {{{ proto static string Locale::setDefault( string $locale ) +* sets the default locale +* }}} */ +/* {{{ proto static string locale_set_default( string $locale ) +* sets the default locale +*/ +PHP_NAMED_FUNCTION(zif_locale_set_default) +{ + char* locale_name = NULL; + int len=0; + + // Parse parameters. + if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s", + &locale_name ,&len ) == FAILURE) + { + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "locale_set_default: unable to parse input params", 0 TSRMLS_CC ); + + RETURN_FALSE; + } + + if(len == 0) { + locale_name = (char *)uloc_getDefault() ; + len = strlen(locale_name); + } + + zend_alter_ini_entry(LOCALE_INI_NAME, sizeof(LOCALE_INI_NAME), locale_name, len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME); + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ +* Gets the value from ICU +* common code shared by get_primary_language,get_script or get_region or get_variant +* result = 0 if error, 1 if successful , -1 if no value +*/ +static char* get_icu_value_internal( char* loc_name , char* tag_name, int* result , int fromParseLocale) +{ + char* tag_value = NULL; + int32_t tag_value_len = 512; + + int singletonPos = 0; + char* mod_loc_name = NULL; + int grOffset = 0; + + int32_t buflen = 512; + UErrorCode status = U_ZERO_ERROR; + + + if( tag_name != LOC_CANONICALIZE_TAG ){ + //Handle grandfathered languages + grOffset = findOffset( LOC_GRANDFATHERED , loc_name ); + if( grOffset >= 0 ){ + if( strcmp(tag_name , LOC_LANG_TAG)==0 ){ + tag_value = estrdup(loc_name); + return tag_value; + } else { + //Since Grandfathered , no value , do nothing , retutn NULL + return NULL; + } + } + + if( fromParseLocale==1 ){ + //Handle singletons + if( strcmp(tag_name , LOC_LANG_TAG)==0 ){ + if( strlen(loc_name)>1 && (isIDPrefix(loc_name) ==1 ) ){ + return loc_name; + } + } + + singletonPos = getSingletonPos( loc_name ); + if( singletonPos == 0){ + //singleton at start of script, region , variant etc. + //or invalid singleton at start of language + return NULL; + } else if( singletonPos > 0 ){ + //singleton at some position except at start + //strip off the singleton and rest of the loc_name + mod_loc_name = estrndup ( loc_name , singletonPos-1); + } + }//end of if fromParse + + }//end of if != LOC_CANONICAL_TAG + + if( mod_loc_name == NULL){ + mod_loc_name = estrdup(loc_name ); + } + +//Proceed to ICU + do{ + tag_value = erealloc( tag_value , buflen ); + tag_value_len = buflen; + + if( strcmp(tag_name , LOC_SCRIPT_TAG)==0 ){ + buflen = uloc_getScript ( mod_loc_name ,tag_value , tag_value_len , &status); + } + if( strcmp(tag_name , LOC_LANG_TAG )==0 ){ + buflen = uloc_getLanguage ( mod_loc_name ,tag_value , tag_value_len , &status); + } + if( strcmp(tag_name , LOC_REGION_TAG)==0 ){ + buflen = uloc_getCountry ( mod_loc_name ,tag_value , tag_value_len , &status); + } + if( strcmp(tag_name , LOC_VARIANT_TAG)==0 ){ + buflen = uloc_getVariant ( mod_loc_name ,tag_value , tag_value_len , &status); + } + if( strcmp(tag_name , LOC_CANONICALIZE_TAG)==0 ){ + buflen = uloc_canonicalize ( mod_loc_name ,tag_value , tag_value_len , &status); + } + + if( U_FAILURE( status ) ) { + if( status == U_BUFFER_OVERFLOW_ERROR ) + { + status = U_ZERO_ERROR; + continue; + } + + //Error in retriving data + *result = 0; + if( tag_value ){ + efree( tag_value ); + } + if( mod_loc_name ){ + efree( mod_loc_name); + } + return NULL; + } + + } while( buflen > tag_value_len ); + + if( buflen ==0 ){ + //No value found + *result = -1; + if( tag_value ){ + efree( tag_value ); + } + if( mod_loc_name ){ + efree( mod_loc_name); + } + return NULL; + } else { + *result = 1; + } + + if( mod_loc_name ){ + efree( mod_loc_name); + } + return tag_value; +} +/* }}} */ + +/* {{{ +* Gets the value from ICU , called when PHP userspace function is called +* common code shared by get_primary_language,get_script or get_region or get_variant +*/ +static void get_icu_value_src_php( char* tag_name, INTERNAL_FUNCTION_PARAMETERS) +{ + + char* loc_name = NULL; + int loc_name_len = 0; + + char* tag_value = NULL; + char* empty_result = ""; + + int result = 0; + char* msg = NULL; + + UErrorCode status = U_ZERO_ERROR; + + intl_error_reset( NULL TSRMLS_CC ); + + // Parse parameters. + if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s", + &loc_name ,&loc_name_len ) == FAILURE) { + spprintf(&msg , 0, "locale_get_%s : unable to parse input params", tag_name ); + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, msg , 1 TSRMLS_CC ); + efree(msg); + + RETURN_NULL(); + } + + if(loc_name_len == 0) { + loc_name = INTL_G(default_locale); + } + + //Call ICU get + tag_value = get_icu_value_internal( loc_name , tag_name , &result ,0); + + //No value found + if( result == -1 ) { + if( tag_value){ + efree( tag_value); + } + RETURN_STRING( empty_result , TRUE); + } + + //value found + if( tag_value){ + RETURN_STRING( tag_value , FALSE); + } + + //Error encountered while fetching the value + if( result ==0) { + spprintf(&msg , 0, "locale_get_%s : unable to get locale %s", tag_name , tag_name ); + intl_error_set( NULL, status, msg , 1 TSRMLS_CC ); + efree(msg); + RETURN_NULL(); + } + +} +/* }}} */ + +/* {{{ + * proto public static string Locale::getScript($locale) + * gets the script for the $locale + }}} */ +/* {{{ + * proto public static string locale_get_script($locale) + * gets the script for the $locale + */ +PHP_FUNCTION( locale_get_script ) +{ + get_icu_value_src_php( LOC_SCRIPT_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU ); +} +/* }}} */ + +/* {{{ + * proto public static string Locale::getRegion($locale) + * gets the region for the $locale + }}} */ +/* {{{ + * proto public static string locale_get_region($locale) + * gets the region for the $locale + */ +PHP_FUNCTION( locale_get_region ) +{ + get_icu_value_src_php( LOC_REGION_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU ); +} +/* }}} */ + +/* {{{ + * proto public static string Locale::getPrimaryLanguage($locale) + * gets the primary language for the $locale + }}} */ +/* {{{ + * proto public static string locale_get_primary_language($locale) + * gets the primary language for the $locale + */ +PHP_FUNCTION(locale_get_primary_language ) +{ + get_icu_value_src_php( LOC_LANG_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU ); +} +/* }}} */ + + +/* {{{ + * common code shared by display_xyz functions to get the value from ICU + }}} */ +static void get_icu_disp_value_src_php( char* tag_name, INTERNAL_FUNCTION_PARAMETERS) +{ + char* loc_name = NULL; + int loc_name_len = 0; + + char* disp_loc_name = NULL; + int disp_loc_name_len = 0; + + UChar* disp_name = NULL; + int32_t disp_name_len = 0; + + char* mod_loc_name = NULL; + + int32_t buflen = 512; + UErrorCode status = U_ZERO_ERROR; + + char* utf8value = NULL; + int utf8value_len = 0; + + char* msg = NULL; + int grOffset = 0; + + intl_error_reset( NULL TSRMLS_CC ); + + // Parse parameters. + if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s|s", + &loc_name, &loc_name_len , + &disp_loc_name ,&disp_loc_name_len ) == FAILURE) + { + spprintf(&msg , 0, "locale_get_display_%s : unable to parse input params", tag_name ); + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, msg , 1 TSRMLS_CC ); + efree(msg); + RETURN_FALSE; + } + + if(loc_name_len == 0) { + loc_name = INTL_G(default_locale); + } + + if( tag_name != DISP_NAME ){ + //Handle grandfathered languages + grOffset = findOffset( LOC_GRANDFATHERED , loc_name ); + if( grOffset >= 0 ){ + if( strcmp(tag_name , LOC_LANG_TAG)==0 ){ + mod_loc_name = getPreferredTag( loc_name ); + } else { + //Since Grandfathered , no value , do nothing , retutn NULL + RETURN_FALSE; + } + } + +/* + int singletonPos = 0; + //Handle singletons + if( (strcmp(tag_name , LOC_LANG_TAG)==0) && isIDPrefix(loc_name) ){ + //return mod_loc_name; + } else { + singletonPos = getSingletonPos( loc_name ); + if( singletonPos == 0){ + //singleton at start of script, region , variant etc. + //or invalid singleton at start of language + RETURN_FALSE; + }else if(singletonPos > 0){ + //singleton at some position except at start + //strip off the singleton and rest of the loc_name + mod_loc_name = estrndup( loc_name , singletonPos-1); + } + } +*/ + }//end of if != LOC_CANONICAL_TAG + + if( mod_loc_name==NULL ){ + mod_loc_name = estrdup( loc_name ); + } + + //Get the disp_value for the given locale + do{ + disp_name = erealloc( disp_name , buflen ); + disp_name_len = buflen; + + //Check if disp_loc_name passed , if not use default locale + if( !disp_loc_name){ + disp_loc_name = estrdup(INTL_G(default_locale)); + } + + if( strcmp(tag_name , LOC_LANG_TAG)==0 ){ + buflen = uloc_getDisplayLanguage ( mod_loc_name , disp_loc_name , disp_name , disp_name_len , &status); + } else if( strcmp(tag_name , LOC_SCRIPT_TAG)==0 ){ + buflen = uloc_getDisplayScript ( mod_loc_name , disp_loc_name , disp_name , disp_name_len , &status); + } else if( strcmp(tag_name , LOC_REGION_TAG)==0 ){ + buflen = uloc_getDisplayCountry ( mod_loc_name , disp_loc_name , disp_name , disp_name_len , &status); + } else if( strcmp(tag_name , LOC_VARIANT_TAG)==0 ){ + buflen = uloc_getDisplayVariant ( mod_loc_name , disp_loc_name , disp_name , disp_name_len , &status); + } else if( strcmp(tag_name , DISP_NAME)==0 ){ + buflen = uloc_getDisplayName ( mod_loc_name , disp_loc_name , disp_name , disp_name_len , &status); + } + + if( U_FAILURE( status ) ) + { + if( status == U_BUFFER_OVERFLOW_ERROR ) + { + status = U_ZERO_ERROR; + continue; + } + + spprintf(&msg, 0, "locale_get_display_%s : unable to get locale %s", tag_name , tag_name ); + intl_error_set( NULL, status, msg , 1 TSRMLS_CC ); + efree(msg); + if( disp_name){ + efree( disp_name ); + } + if( mod_loc_name){ + efree( mod_loc_name ); + } + RETURN_FALSE; + } + } while( buflen > disp_name_len ); + + if( mod_loc_name){ + efree( mod_loc_name ); + } + // Convert display locale name from UTF-16 to UTF-8. + intl_convert_utf16_to_utf8( &utf8value, &utf8value_len, disp_name, buflen, &status ); + efree( disp_name ); + if( U_FAILURE( status ) ) + { + spprintf(&msg, 0, "locale_get_display_%s :error converting display name for %s to UTF-8", tag_name , tag_name ); + intl_error_set( NULL, status, msg , 1 TSRMLS_CC ); + efree(msg); + RETURN_FALSE; + } + + RETVAL_STRINGL( utf8value, utf8value_len , FALSE); + +} +/* }}} */ + +/* {{{ +* public static string Locale::getDisplayName($locale, $in_locale = null) +* gets the name for the $locale in $in_locale or default_locale + }}} */ +/* {{{ +* public static string get_display_name($locale, $in_locale = null) +* gets the name for the $locale in $in_locale or default_locale +*/ +PHP_FUNCTION(locale_get_display_name) +{ + get_icu_disp_value_src_php( DISP_NAME , INTERNAL_FUNCTION_PARAM_PASSTHRU ); +} +/* }}} */ + +/* {{{ +* public static string Locale::getDisplayLanguage($locale, $in_locale = null) +* gets the language for the $locale in $in_locale or default_locale + }}} */ +/* {{{ +* public static string get_display_language($locale, $in_locale = null) +* gets the language for the $locale in $in_locale or default_locale +*/ +PHP_FUNCTION(locale_get_display_language) +{ + get_icu_disp_value_src_php( LOC_LANG_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU ); +} +/* }}} */ + +/* {{{ +* public static string Locale::getDisplayScript($locale, $in_locale = null) +* gets the script for the $locale in $in_locale or default_locale + }}} */ +/* {{{ +* public static string get_display_script($locale, $in_locale = null) +* gets the script for the $locale in $in_locale or default_locale +*/ +PHP_FUNCTION(locale_get_display_script) +{ + get_icu_disp_value_src_php( LOC_SCRIPT_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU ); +} +/* }}} */ + +/* {{{ +* public static string Locale::getDisplayRegion($locale, $in_locale = null) +* gets the region for the $locale in $in_locale or default_locale + }}} */ +/* {{{ +* public static string get_display_region($locale, $in_locale = null) +* gets the region for the $locale in $in_locale or default_locale +*/ +PHP_FUNCTION(locale_get_display_region) +{ + get_icu_disp_value_src_php( LOC_REGION_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU ); +} +/* }}} */ + +/* {{{ +* public static string Locale::getDisplayVariant($locale, $in_locale = null) +* gets the variant for the $locale in $in_locale or default_locale + }}} */ +/* {{{ +* public static string get_display_variant($locale, $in_locale = null) +* gets the variant for the $locale in $in_locale or default_locale +*/ +PHP_FUNCTION(locale_get_display_variant) +{ + get_icu_disp_value_src_php( LOC_VARIANT_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU ); +} +/* }}} */ + + /* {{{ + * proto static string[] getKeywords(string $locale) { + * return an associative array containing keyword-value + * pairs for this locale. The keys are keys to the array (doh!) + * }}}*/ + /* {{{ + * proto static string[] locale_get_keywords(string $locale) { + * return an associative array containing keyword-value + * pairs for this locale. The keys are keys to the array (doh!) + */ + +PHP_FUNCTION( locale_get_keywords ) +{ + UEnumeration* e = NULL; + UErrorCode status = U_ZERO_ERROR; + + const char* kw_key = NULL; + int32_t kw_key_len = 0; + + char* loc_name = NULL; + int loc_name_len = 0; + +/* + ICU expects the buffer to be allocated before calling the function + and so the buffer size has been explicitly specified + ICU uloc.h #define ULOC_KEYWORD_AND_VALUES_CAPACITY 100 + hence the kw_value buffer size is 100 +*/ + char* kw_value = NULL; + int32_t kw_value_len = 100; + + intl_error_reset( NULL TSRMLS_CC ); + + // Parse parameters. + if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s", + &loc_name, &loc_name_len ) == FAILURE) + { + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "locale_get_keywords: unable to parse input params", 0 TSRMLS_CC ); + + RETURN_FALSE; + } + + if(loc_name_len == 0) { + loc_name = INTL_G(default_locale); + } + + //Get the keywords + e = uloc_openKeywords( loc_name, &status ); + if( e != NULL ) + { + // Traverse it, filling the return array. + array_init( return_value ); + + while( ( kw_key = uenum_next( e, &kw_key_len, &status ) ) != NULL ){ + kw_value = ecalloc( 1 , kw_value_len ); + + //Get the keyword value for each keyword + kw_value_len=uloc_getKeywordValue( loc_name,kw_key, kw_value, kw_value_len , &status ); + if (status == U_BUFFER_OVERFLOW_ERROR) { + status = U_ZERO_ERROR; + kw_value = erealloc( kw_value , kw_value_len+1); + kw_value_len=uloc_getKeywordValue( loc_name,kw_key, kw_value, kw_value_len+1 , &status ); + } else if(!U_FAILURE(status)) { + kw_value = erealloc( kw_value , kw_value_len+1); + } + if (U_FAILURE(status)) { + intl_error_set( NULL, FAILURE, "locale_get_keywords: Error encountered while getting the keyword value for the keyword", 0 TSRMLS_CC ); + if( kw_value){ + efree( kw_value ); + } + zval_dtor(return_value); + RETURN_FALSE; + } + + add_assoc_stringl( return_value, (char *)kw_key, kw_value , kw_value_len, 0); + } //end of while + + }//end of if e!=NULL + + uenum_close( e ); +} +/* }}} */ + + /* {{{ + * proto static string Locale::canonicalize($locale) + * @return string the canonicalized locale + * }}} */ + /* {{{ + * proto static string locale_canonicalize(Locale $loc, string $locale) + * @param string $locale The locale string to canonicalize + */ +PHP_FUNCTION(locale_canonicalize) +{ + get_icu_value_src_php( LOC_CANONICALIZE_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU ); +} +/* }}} */ + + +/* {{{ append_key_value +* Internal function which is called from locale_compose +* gets the value for the key_name and appends to the loc_name +* returns 1 if successful , -1 if not found , +* 0 if array element is not a string , -2 if buffer-overflow +*/ +static int append_key_value(char* loc_name, int loc_name_capacity , HashTable* hash_arr, char* key_name TSRMLS_DC) +{ + int needed_size = -1; + zval** ele_value = NULL; + + intl_error_reset( NULL TSRMLS_CC ); + + if( zend_hash_find( hash_arr , key_name , strlen(key_name) + 1 ,(void **)&ele_value ) == SUCCESS ){ + if( Z_TYPE_PP(ele_value)!= IS_STRING ){ + //element value is not a string + return 0; + } + if( strcmp(key_name , LOC_LANG_TAG) != 0 && + strcmp(key_name , LOC_GRANDFATHERED_LANG_TAG)!=0 ){ + + needed_size = Z_STRLEN_PP(ele_value)+1 ; + if( needed_size > loc_name_capacity ){ + //Will cause Buffer_overflow + return -2; + + } else { + strcat( loc_name , SEPARATOR); + strncat( loc_name , + Z_STRVAL_PP(ele_value) , + Z_STRLEN_PP(ele_value) ); + } + } else { + //lang or grandfathered tag + needed_size = Z_STRLEN_PP(ele_value) ; + if( needed_size > loc_name_capacity ){ + //Will cause Buffer_overflow + return -2; + + } else { + strncat( loc_name , Z_STRVAL_PP(ele_value) , Z_STRLEN_PP(ele_value) ); + } + } + + return 1; + } + + return -1; +} +/* }}} */ + +/* {{{ append_prefix , appends the prefix needed +* e.g. private adds 'x' +*/ +static void add_prefix(char* loc_name , char* key_name) +{ + if( strncmp(key_name , LOC_PRIVATE_TAG , 7) == 0 ){ + strcat( loc_name , SEPARATOR); + strcat( loc_name , PRIVATE_PREFIX); + } +} +/* }}} */ + +/* {{{ append_multiple_key_values +* Internal function which is called from locale_compose +* gets the multiple values for the key_name and appends to the loc_name +* used for 'variant','extlang','private' +* returns 1 if successful , -1 if not found , +* 0 if array element is not a string , -2 if buffer-overflow +*/ +static int append_multiple_key_values(char* loc_name, int loc_name_capacity , HashTable* hash_arr, char* key_name TSRMLS_DC) +{ + int result = -1; + zval** ele_value = NULL; + char* cur_key_name = NULL; + + int i = 0; + int isFirstSubtag = 0; + int max_value = 0; + int needed_size = 0; + + //Variant/ Extlang/Private etc. + if( zend_hash_find( hash_arr , key_name , strlen(key_name) + 1 ,(void **)&ele_value ) == SUCCESS ){ + if( Z_TYPE_PP(ele_value)!= IS_STRING ){ + //key_name is not a string + return 0; + } + + //Determine the needed_size and check it against available + if( strcmp(key_name , LOC_PRIVATE_TAG) ==0 ){ + //for the string "-x-" and the size of array element + needed_size = Z_STRLEN_PP(ele_value) + 3; + } else { + //for the SEPARATOR and the size of array element + needed_size = Z_STRLEN_PP(ele_value) + 1; + } + if( needed_size > loc_name_capacity){ + //Will cause buffer_overflow + return -2; + } + + add_prefix( loc_name , key_name); + + strcat( loc_name , SEPARATOR); + strncat( loc_name , Z_STRVAL_PP(ele_value) , Z_STRLEN_PP(ele_value) ); + return 1; + } else { + //Decide the max_value : the max. no. of elements allowed + if( strcmp(key_name , LOC_VARIANT_TAG) ==0 ){ + max_value = MAX_NO_VARIANT; + } + if( strcmp(key_name , LOC_EXTLANG_TAG) ==0 ){ + max_value = MAX_NO_EXTLANG; + } + if( strcmp(key_name , LOC_PRIVATE_TAG) ==0 ){ + max_value = MAX_NO_PRIVATE; + } + + //Multiple variant values as variant0, variant1 ,variant2 + cur_key_name = (char*)ecalloc( 25, 25); + isFirstSubtag = 0; + for( i=0 ; i< max_value; i++ ){ + sprintf( cur_key_name , "%s%d", key_name , i); + if( zend_hash_find( hash_arr , cur_key_name , strlen(cur_key_name) + 1,(void **)&ele_value ) == SUCCESS ){ + if( Z_TYPE_PP(ele_value)!= IS_STRING ){ + //variant is not a string + if( cur_key_name){ + efree(cur_key_name); + } + return 0; + } + + //Determine the needed_size and check it against available + if( strcmp(key_name , LOC_PRIVATE_TAG) ==0 && isFirstSubtag ==0 ){ + //for the string "-x-" and the size of array element + needed_size = Z_STRLEN_PP(ele_value) + 3; + } else { + //for the SEPARATOR and the size of array element + needed_size = Z_STRLEN_PP(ele_value) + 1; + } + if( needed_size > loc_name_capacity){ + //Will cause buffer_overflow + if( cur_key_name){ + efree(cur_key_name); + } + return -2; + } + + //Add the contents + if (isFirstSubtag++ == 0){ + add_prefix( loc_name , cur_key_name); + } + strcat( loc_name , SEPARATOR); + strncat( loc_name , Z_STRVAL_PP(ele_value) , Z_STRLEN_PP(ele_value) ); + } + result = 1; + }//end of for + efree(cur_key_name); + }//end of else + + return result; +} +/* }}} */ + +/*{{{ +* If applicable sets error message and aborts locale_compose gracefully +* returns 0 if locale_compose needs to be aborted +* otherwise returns 1 +*/ +static int handleAppendResult( int result,char* loc_name TSRMLS_DC) +{ + intl_error_reset( NULL TSRMLS_CC ); + if( result == 0 ){ + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "Aborting locale_compose: parameter array element is not a string ", 0 TSRMLS_CC ); + + if( loc_name){ + efree(loc_name); + } + return 0; + } + if( result == -2 ){ + intl_error_set( NULL, U_BUFFER_OVERFLOW_ERROR, + "Aborting locale_compose: array element will cause the buffer overflow. Maximum size allowed for locale_compose parameters is 512 bytes including separator character and prefixes. ", 0 TSRMLS_CC ); + + if( loc_name){ + efree(loc_name); + } + return 0; + } + return 1; +} +/* }}} */ + +/* {{{ +* proto static string Locale::composeLocale($array) +* Creates a locale by combining the parts of locale-ID passed +* }}} */ +/* {{{ +* proto static string compose_locale($array) +* Creates a locale by combining the parts of locale-ID passed +* }}} */ +PHP_FUNCTION(locale_compose) +{ + char* loc_name = NULL; + int loc_name_len = 0; + + int32_t buflen = 512; + + zval* arr = NULL; + HashTable* hash_arr = NULL; + + int result = 0; + + intl_error_reset( NULL TSRMLS_CC ); + + // Parse parameters. + if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "a", + &arr) == FAILURE) + { + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "locale_compose: unable to parse input params", 0 TSRMLS_CC ); + + RETURN_FALSE; + } + + hash_arr = HASH_OF( arr ); + + if( !hash_arr || zend_hash_num_elements( hash_arr ) == 0 ) + RETURN_FALSE; + + //Allocate memory + loc_name = (char*)ecalloc( 512, sizeof(char)); + loc_name_len = buflen; + + //Check for grandfathered first + result = append_key_value( loc_name , loc_name_len , hash_arr , LOC_GRANDFATHERED_LANG_TAG TSRMLS_CC ); + if( result == 1 ){ + RETURN_STRING( loc_name ,FALSE); + } + if( !handleAppendResult( result, loc_name TSRMLS_CC)){ + RETURN_FALSE; + } + + //Not grandfathered + result = append_key_value( loc_name , loc_name_len , hash_arr , LOC_LANG_TAG TSRMLS_CC ); + if( result == -1 ){ + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "locale_compose: parameter array does not contain 'language' tag.", 0 TSRMLS_CC ); + if( loc_name){ + efree(loc_name); + } + RETURN_FALSE; + } + if( !handleAppendResult( result, loc_name TSRMLS_CC)){ + RETURN_FALSE; + } + + //Extlang + result = append_multiple_key_values( loc_name , loc_name_len , hash_arr , LOC_EXTLANG_TAG TSRMLS_CC ); + if( !handleAppendResult( result, loc_name TSRMLS_CC)){ + RETURN_FALSE; + } + + //Script + result = append_key_value( loc_name , loc_name_len , hash_arr , LOC_SCRIPT_TAG TSRMLS_CC ); + if( !handleAppendResult( result, loc_name TSRMLS_CC)){ + RETURN_FALSE; + } + + //Region + result = append_key_value( loc_name , loc_name_len , hash_arr , LOC_REGION_TAG TSRMLS_CC ); + if( !handleAppendResult( result, loc_name TSRMLS_CC)){ + RETURN_FALSE; + } + + //Variant + result = append_multiple_key_values( loc_name , loc_name_len , hash_arr , LOC_VARIANT_TAG TSRMLS_CC ); + if( !handleAppendResult( result, loc_name TSRMLS_CC)){ + RETURN_FALSE; + } + + //Private + result = append_multiple_key_values( loc_name , loc_name_len , hash_arr , LOC_PRIVATE_TAG TSRMLS_CC ); + if( !handleAppendResult( result, loc_name TSRMLS_CC)){ + RETURN_FALSE; + } + + RETURN_STRING( loc_name , FALSE); +} +/* }}} */ + + +/*{{{ +* Parses the locale and returns private subtags if existing +* else returns NULL +* e.g. for locale='en_US-x-prv1-prv2-prv3' +* returns a pointer to the string 'prv1-prv2-prv3' +*/ +static char* get_private_subtags(char* loc_name) +{ + char* result =NULL; + int singletonPos = 0; + int len =0; + char* mod_loc_name =NULL; + + if( loc_name && (len = strlen(loc_name)>0 ) ){ + mod_loc_name = loc_name ; + len = strlen(mod_loc_name); + while( (singletonPos = getSingletonPos(mod_loc_name))!= -1){ + + if( singletonPos!=-1){ + if( (*(mod_loc_name+singletonPos)=='x') || (*(mod_loc_name+singletonPos)=='X') ){ + //private subtag start found + if( singletonPos + 2 == len){ + //loc_name ends with '-x-' ; return NULL + } + else{ + //result = mod_loc_name + singletonPos +2; + result = estrndup(mod_loc_name + singletonPos+2 , (len -( singletonPos +2) ) ); + } + break; + } + else{ + if( singletonPos + 1 >= len){ + //String end + break; + } else { + //singleton found but not a private subtag , hence check further in the string for the private subtag + mod_loc_name = mod_loc_name + singletonPos +1; + len = strlen(mod_loc_name); + } + } + } + + }//end of while + } + + return result; +} +/* }}} */ + +/* {{{ code used by locale_parse +*/ +static int add_array_entry(char* loc_name, zval* hash_arr, char* key_name TSRMLS_DC) +{ + char* key_value = NULL; + char* cur_key_name = NULL; + char* token = NULL; + char* last_ptr = NULL; + + int result = 0; + int cur_result = 0; + int cnt = 0; + + + if( strcmp(key_name , LOC_PRIVATE_TAG)==0 ){ + key_value = get_private_subtags( loc_name ); + result = 1; + } else { + key_value = get_icu_value_internal( loc_name , key_name , &result,1 ); + } + if( (strcmp(key_name , LOC_PRIVATE_TAG)==0) || + ( strcmp(key_name , LOC_VARIANT_TAG)==0) ){ + if( result > 0 && key_value){ + //Tokenize on the "_" or "-" + token = php_strtok_r( key_value , DELIMITER ,&last_ptr); + if( cur_key_name ){ + efree( cur_key_name); + } + cur_key_name = (char*)ecalloc( 25, 25); + sprintf( cur_key_name , "%s%d", key_name , cnt++); + add_assoc_string( hash_arr, cur_key_name , token ,TRUE ); + //tokenize on the "_" or "-" and stop at singleton if any + while( (token = php_strtok_r(NULL , DELIMITER , &last_ptr)) && (strlen(token)>1) ){ + sprintf( cur_key_name , "%s%d", key_name , cnt++); + add_assoc_string( hash_arr, cur_key_name , token , TRUE ); + } + + if( key_name == LOC_PRIVATE_TAG ){ + } + } + } else { + if( result == 1 ){ + add_assoc_string( hash_arr, key_name , key_value , TRUE ); + cur_result = 1; + } + } + + if( cur_key_name ){ + efree( cur_key_name); + } + //if( key_name != LOC_PRIVATE_TAG && key_value){ + if( key_value){ + efree(key_value); + } + return cur_result; +} + +/* {{{ +* proto static array Locale::parseLocale($locale) +* parses a locale-id into an array the different parts of it + }}} */ +/* {{{ +* proto static array parse_locale($locale) +* parses a locale-id into an array the different parts of it +*/ +PHP_FUNCTION(locale_parse) +{ + char* loc_name = NULL; + int loc_name_len = 0; + int grOffset = 0; + + intl_error_reset( NULL TSRMLS_CC ); + + // Parse parameters. + if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s", + &loc_name, &loc_name_len ) == FAILURE) + { + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "locale_parse: unable to parse input params", 0 TSRMLS_CC ); + + RETURN_FALSE; + } + + if(loc_name_len == 0) { + loc_name = INTL_G(default_locale); + } + + array_init( return_value ); + + grOffset = findOffset( LOC_GRANDFATHERED , loc_name ); + if( grOffset >= 0 ){ + add_assoc_string( return_value , LOC_GRANDFATHERED_LANG_TAG , estrdup(loc_name) ,FALSE ); + } + else{ + //Not grandfathered + add_array_entry( loc_name , return_value , LOC_LANG_TAG TSRMLS_CC); + add_array_entry( loc_name , return_value , LOC_SCRIPT_TAG TSRMLS_CC); + add_array_entry( loc_name , return_value , LOC_REGION_TAG TSRMLS_CC); + add_array_entry( loc_name , return_value , LOC_VARIANT_TAG TSRMLS_CC); + add_array_entry( loc_name , return_value , LOC_PRIVATE_TAG TSRMLS_CC); + } +} +/* }}} */ + +/* {{{ proto static array Locale::getAllVariants($locale) +* gets an array containing the list of variants, or null + }}} */ +/* {{{ proto static array locale_get_all_variants($locale) +* gets an array containing the list of variants, or null +*/ +PHP_FUNCTION(locale_get_all_variants) +{ + char* loc_name = NULL; + int loc_name_len = 0; + + int result = 0; + char* token = NULL; + char* variant = NULL; + char* saved_ptr = NULL; + + intl_error_reset( NULL TSRMLS_CC ); + + // Parse parameters. + if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s", + &loc_name, &loc_name_len ) == FAILURE) + { + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "locale_parse: unable to parse input params", 0 TSRMLS_CC ); + + RETURN_FALSE; + } + + if(loc_name_len == 0) { + loc_name = INTL_G(default_locale); + } + + + array_init( return_value ); + + //If the locale is grandfathered , stop , no variants + if( findOffset( LOC_GRANDFATHERED , loc_name ) >= 0 ){ + //("Grandfathered Tag. No variants."); + } + else { + //Call ICU variant + variant = get_icu_value_internal( loc_name , LOC_VARIANT_TAG , &result ,0); + if( result > 0 && variant){ + //Tokenize on the "_" or "-" + token = php_strtok_r( variant , DELIMITER , &saved_ptr); + add_next_index_stringl( return_value, token , strlen(token) ,TRUE ); + //tokenize on the "_" or "-" and stop at singleton if any + while( (token = php_strtok_r(NULL , DELIMITER, &saved_ptr)) && (strlen(token)>1) ){ + add_next_index_stringl( return_value, token , strlen(token) ,TRUE ); + } + } + if( variant ){ + efree( variant ); + } + } + + +} +/* }}} */ + +/*{{{ +* Converts to lower case and also replaces all hyphuns with the underscore +*/ +static int strToMatch(char* str ,char *retstr) +{ + char* anchor = NULL; + char* anchor1 = NULL; + int result = 0; + int len = 0; + + if( (!str) || strlen(str) ==0){ + return result; + } else { + anchor = retstr; + anchor1 = str; + len = strlen(str); + while( (*str)!='\0' ){ + if( *str == '-' ){ + *retstr = '_'; + } else { + *retstr = tolower(*str); + } + str++; + retstr++; + } + *retstr = '\0'; + retstr= anchor; + str= anchor1; + result = 1; + } + + return(result); +} +/* }}} */ + +/*{{{ +* code used by locale_filter_maatches +*/ +PHP_FUNCTION(locale_filter_matches) +{ + char* lang_tag = NULL; + int lang_tag_len = 0; + char* loc_range = NULL; + int loc_range_len = 0; + + int result = 0; + char* token = 0; + char* chrcheck = NULL; + + char* can_lang_tag = NULL; + char* can_loc_range = NULL; + + char* cur_lang_tag = NULL; + char* cur_loc_range = NULL; + + zend_bool boolCanonical = 0; + UErrorCode status = U_ZERO_ERROR; + + intl_error_reset( NULL TSRMLS_CC ); + + // Parse parameters. + if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "ssb", + &lang_tag, &lang_tag_len , &loc_range , &loc_range_len , + &boolCanonical) == FAILURE) + { + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "locale_filter_matches: unable to parse input params", 0 TSRMLS_CC ); + + RETURN_FALSE; + } + + if(loc_range_len == 0) { + loc_range = INTL_G(default_locale); + } + + if( strcmp(loc_range,"*")==0){ + RETURN_TRUE; + } + + if( boolCanonical ){ + //canonicalize loc_range + can_loc_range=get_icu_value_internal( loc_range , LOC_CANONICALIZE_TAG , &result , 0); + if( result ==0) { + intl_error_set( NULL, status, + "locale_filter_matches : unable to canonicalize loc_range" , 0 TSRMLS_CC ); + RETURN_FALSE; + } + + //canonicalize lang_tag + can_lang_tag = get_icu_value_internal( lang_tag , LOC_CANONICALIZE_TAG , &result , 0); + if( result ==0) { + intl_error_set( NULL, status, + "locale_filter_matches : unable to canonicalize lang_tag" , 0 TSRMLS_CC ); + RETURN_FALSE; + } + + //Convert to lower case for case-insensitive comparison + cur_lang_tag = ecalloc( 1, strlen(lang_tag ) + 1); + + //Convert to lower case for case-insensitive comparison + result = strToMatch( can_lang_tag , cur_lang_tag); + if( result == 0) { + efree( cur_lang_tag ); + efree( can_lang_tag ); + RETURN_FALSE; + } + + cur_loc_range = ecalloc( 1, strlen(loc_range ) + 1); + result = strToMatch( can_loc_range , cur_loc_range ); + if( result == 0) { + efree( cur_lang_tag ); + efree( can_lang_tag ); + efree( cur_loc_range ); + efree( can_loc_range ); + RETURN_FALSE; + } + + //check if prefix + token = strstr( cur_lang_tag , cur_loc_range ); + + if( token && (token==cur_lang_tag) ){ + //check if the char. after match is SEPARATOR + chrcheck = token + (strlen(cur_loc_range)); + if( isIDSeparator(*chrcheck) || isEndOfTag(*chrcheck) ){ + if( cur_lang_tag){ + efree( cur_lang_tag ); + } + if( cur_loc_range){ + efree( cur_loc_range ); + } + if( can_lang_tag){ + efree( can_lang_tag ); + } + if( can_loc_range){ + efree( can_loc_range ); + } + RETURN_TRUE; + } + } + + //No prefix as loc_range + if( cur_lang_tag){ + efree( cur_lang_tag ); + } + if( cur_loc_range){ + efree( cur_loc_range ); + } + if( can_lang_tag){ + efree( can_lang_tag ); + } + if( can_loc_range){ + efree( can_loc_range ); + } + RETURN_FALSE; + + }//end of if isCanonical + else{ + //Convert to lower case for case-insensitive comparison + cur_lang_tag = ecalloc( 1, strlen(lang_tag ) + 1); + + result = strToMatch( lang_tag , cur_lang_tag); + if( result == 0) { + efree( cur_lang_tag ); + RETURN_FALSE; + } + cur_loc_range = ecalloc( 1, strlen(loc_range ) + 1); + result = strToMatch( loc_range , cur_loc_range ); + if( result == 0) { + efree( cur_lang_tag ); + efree( cur_loc_range ); + RETURN_FALSE; + } + + //check if prefix + token = strstr( cur_lang_tag , cur_loc_range ); + + if( token && (token==cur_lang_tag) ){ + //check if the char. after match is SEPARATOR + chrcheck = token + (strlen(cur_loc_range)); + if( isIDSeparator(*chrcheck) || isEndOfTag(*chrcheck) ){ + if( cur_lang_tag){ + efree( cur_lang_tag ); + } + if( cur_loc_range){ + efree( cur_loc_range ); + } + RETURN_TRUE; + } + } + + //No prefix as loc_range + if( cur_lang_tag){ + efree( cur_lang_tag ); + } + if( cur_loc_range){ + efree( cur_loc_range ); + } + RETURN_FALSE; + + } +} + +static void array_cleanup( char* arr[] , int arr_size) +{ + int i=0; + for( i=0; i< arr_size; i++ ){ + if( arr[i] ){ + efree( arr[i]); + } + } + +} + +/* {{{ +* returns the lookup result to lookup_loc_range_src_php +* internal function +*/ +static char* lookup_loc_range(char* loc_range, HashTable* hash_arr , int isCanonical TSRMLS_DC) +{ + int cur_arr_ind = 0; + int i = 0; + int cur_arr_len = 0; + int result = 0; + + char* lang_tag = NULL; + zval** ele_value = NULL; + char* cur_arr[MAX_NO_LOOKUP_LANG_TAG] ; + + char* loc_range_to_cmp = NULL; + char* cur_loc_range = NULL; + char* can_loc_range = NULL; + int saved_pos = 0; + + char* return_value = NULL; + UErrorCode status = U_ZERO_ERROR; + char* empty_result = ""; + + //convert the array to lowercase , also replace hyphuns with the underscore and store it in cur_arr + for(zend_hash_internal_pointer_reset(hash_arr); + zend_hash_has_more_elements(hash_arr) == SUCCESS; + zend_hash_move_forward(hash_arr)) { + + if (zend_hash_get_current_data(hash_arr, (void**)&ele_value) == FAILURE) { + // Should never actually fail + // since the key is known to exist. + continue; + } + if( Z_TYPE_PP(ele_value)!= IS_STRING ){ + //element value is not a string + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "lookup_loc_range: array element is not a string ", + 0 TSRMLS_CC ); + return NULL; + } else { + if( lang_tag ){ + efree( lang_tag ); + } + lang_tag = estrdup(Z_STRVAL_PP(ele_value) ); + + if( isCanonical ==0 ){ + //+1 for the terminating '\0' + cur_arr[cur_arr_ind] = ecalloc(1, strlen(lang_tag)+1 ); + result = strToMatch(lang_tag, cur_arr[cur_arr_ind]) ; + } + else{ + cur_arr[cur_arr_ind] = estrdup(lang_tag); + } + + + if( cur_arr_ind < MAX_NO_LOOKUP_LANG_TAG ){ + cur_arr_ind++ ; + } + else{ + break; + } + } + }//end of for + if( lang_tag ){ + efree( lang_tag ); + } + + cur_arr_len = cur_arr_ind; + + //Canonicalize array elements + if( isCanonical ==1 ){ + for( i=0; i< cur_arr_ind; i++ ){ + lang_tag =get_icu_value_internal( cur_arr[i] , LOC_CANONICALIZE_TAG , &result , 0); + efree( cur_arr[i] ); + cur_arr[i] = ecalloc(1, strlen(lang_tag)+1 ); + result = strToMatch(lang_tag, cur_arr[i]) ; + efree( lang_tag); + if( result ==0) { + intl_error_set( NULL, status, + "locale_lookup : unable to canonicalize lang_tag" , 0 TSRMLS_CC ); + if( lang_tag ){ + efree( lang_tag ); + } + array_cleanup( cur_arr , cur_arr_len ); + return NULL; + } + } + + } + + if( isCanonical ==1 ){ + //Canonicalize the loc_range + can_loc_range =get_icu_value_internal( loc_range, LOC_CANONICALIZE_TAG , &result , 0); + if( result != 1 ){ + //Error + intl_error_set( NULL, status, + "locale_lookup : unable to canonicalize loc_range" , 0 TSRMLS_CC ); + if( lang_tag ){ + efree( lang_tag ); + } + if( can_loc_range ){ + efree( can_loc_range ); + } + array_cleanup( cur_arr , cur_arr_len ); + return NULL; + } else { + //convert to lower and replace hyphuns + cur_loc_range = ecalloc( 1, strlen(can_loc_range) +1); + result = strToMatch(can_loc_range , cur_loc_range); + } + } else { + cur_loc_range = ecalloc( 1, strlen(loc_range) +1); + //convert to lower and replace hyphuns + result = strToMatch(loc_range , cur_loc_range); + } + + + //Lookup for the lang_tag match + saved_pos = strlen(cur_loc_range); + while(saved_pos!=(-1) ){ + if( loc_range_to_cmp){ + efree(loc_range_to_cmp); + } + loc_range_to_cmp = estrndup(cur_loc_range,saved_pos+1); + for( i=0; i< cur_arr_ind; i++ ){ + if( cur_arr[i] && (strcmp(loc_range_to_cmp,cur_arr[i])==0) ){ + //Match found + return_value = estrdup( cur_arr[i] ); + if ( cur_loc_range ){ + efree( cur_loc_range ); + } + if( loc_range_to_cmp){ + efree(loc_range_to_cmp); + } + if( can_loc_range ){ + efree( can_loc_range ); + } + array_cleanup( cur_arr , cur_arr_len ); + return return_value; + } + } + saved_pos = getStrrtokenPos( cur_loc_range , saved_pos ); + } + + if( loc_range_to_cmp){ + efree(loc_range_to_cmp); + } + if ( cur_loc_range ){ + efree( cur_loc_range ); + } + if( can_loc_range ){ + efree( can_loc_range ); + } + array_cleanup( cur_arr , cur_arr_len ); + //Match not found + return empty_result; + +} +/* }}} */ + +/* {{{ +* public static function lookup(array $langtag, $locale, $default = null) +* Searchs the items in $langtag for the best match to the language +* range +*/ +/* {{{ +* public static function lookup(array $langtag, $locale, $default = null) +* Searchs the items in $langtag for the best match to the language +* range +*/ +PHP_FUNCTION(locale_lookup) +{ + char* fallback_loc = NULL; + int fallback_loc_len = 0; + char* loc_range = NULL; + int loc_range_len = 0; + + zval* arr = NULL; + HashTable* hash_arr = NULL; + zend_bool boolCanonical = 0; + + char* result =NULL; + + intl_error_reset( NULL TSRMLS_CC ); + + // Parse parameters. + if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "asb|s", + &arr,&loc_range,&loc_range_len,&boolCanonical, + &fallback_loc,&fallback_loc_len) == FAILURE) + { + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "locale_lookup: unable to parse input params", 0 TSRMLS_CC ); + + RETURN_NULL(); + } + + + if(loc_range_len == 0) { + loc_range = INTL_G(default_locale); + } + + + //MAKE_STD_ZVAL(hash_arr); + hash_arr = HASH_OF( arr ); + + if( !hash_arr || zend_hash_num_elements( hash_arr ) == 0 ){ + RETURN_EMPTY_STRING(); + } + else{ + result = lookup_loc_range( loc_range ,hash_arr ,(boolCanonical?1:0) TSRMLS_CC); + + if(result == NULL || result[0] == '\0') { + if( fallback_loc ) { + result = estrndup( fallback_loc , fallback_loc_len); + } else { + RETURN_EMPTY_STRING(); + } + } +} + + RETVAL_STRINGL( result, strlen(result), 0); +} + + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + *can_loc_len +*/ |
