Back to index

php5  5.3.10
locale_methods.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | PHP Version 5                                                        |
00004    +----------------------------------------------------------------------+
00005    | This source file is subject to version 3.01 of the PHP license,      |
00006    | that is bundled with this package in the file LICENSE, and is        |
00007    | available through the world-wide-web at the following url:           |
00008    | http://www.php.net/license/3_01.txt                                  |
00009    | If you did not receive a copy of the PHP license and are unable to   |
00010    | obtain it through the world-wide-web, please send a note to          |
00011    | license@php.net so we can mail you a copy immediately.               |
00012    +----------------------------------------------------------------------+
00013    | Authors: Kirti Velankar <kirtig@yahoo-inc.com>                       |
00014    +----------------------------------------------------------------------+
00015 */
00016 
00017 /* $Id: locale_methods.c 319676 2011-11-22 12:29:15Z felipe $ */
00018 
00019 #ifdef HAVE_CONFIG_H
00020 #include "config.h"
00021 #endif
00022 
00023 #include <unicode/ustring.h>
00024 #include <unicode/udata.h>
00025 #include <unicode/putil.h>
00026 #include <unicode/ures.h>
00027 
00028 #include "php_intl.h"
00029 #include "locale.h"
00030 #include "locale_class.h"
00031 #include "locale_methods.h"
00032 #include "intl_convert.h"
00033 #include "intl_data.h"
00034 
00035 #include <zend_API.h>
00036 #include <zend.h>
00037 #include <php.h>
00038 #include "main/php_ini.h"
00039 #include "ext/standard/php_smart_str.h"
00040 
00041 ZEND_EXTERN_MODULE_GLOBALS( intl )
00042 
00043 /* Sizes required for the strings "variant15" , "extlang11", "private12" etc. */
00044 #define SEPARATOR "_"
00045 #define SEPARATOR1 "-"
00046 #define DELIMITER "-_"
00047 #define EXTLANG_PREFIX "a"
00048 #define PRIVATE_PREFIX "x"
00049 #define DISP_NAME "name"
00050 
00051 #define MAX_NO_VARIANT  15
00052 #define MAX_NO_EXTLANG  3
00053 #define MAX_NO_PRIVATE  15
00054 #define MAX_NO_LOOKUP_LANG_TAG  100
00055 
00056 #define LOC_NOT_FOUND 1
00057 
00058 /* Sizes required for the strings "variant15" , "extlang3", "private12" etc. */
00059 #define VARIANT_KEYNAME_LEN  11
00060 #define EXTLANG_KEYNAME_LEN  10
00061 #define PRIVATE_KEYNAME_LEN  11
00062 
00063 /* Based on IANA registry at the time of writing this code
00064 *
00065 */
00066 static const char * const LOC_GRANDFATHERED[] = {
00067        "art-lojban",        "i-klingon",         "i-lux",                    "i-navajo",          "no-bok",            "no-nyn",
00068        "cel-gaulish",              "en-GB-oed",         "i-ami",             
00069        "i-bnn",             "i-default",         "i-enochian", 
00070        "i-mingo",           "i-pwn",             "i-tao", 
00071        "i-tay",             "i-tsu",             "sgn-BE-fr",
00072        "sgn-BE-nl",         "sgn-CH-de",         "zh-cmn",
00073        "zh-cmn-Hans",              "zh-cmn-Hant",              "zh-gan" ,
00074        "zh-guoyu",          "zh-hakka",          "zh-min",
00075        "zh-min-nan",               "zh-wuu",            "zh-xiang",   
00076        "zh-yue",            NULL
00077 };
00078 
00079 /* Based on IANA registry at the time of writing this code
00080 *  This array lists the preferred values for the grandfathered tags if applicable
00081 *  This is in sync with the array LOC_GRANDFATHERED      
00082 *  e.g. the offsets of the grandfathered tags match the offset of the preferred  value
00083 */
00084 static const int            LOC_PREFERRED_GRANDFATHERED_LEN = 6;
00085 static const char * const   LOC_PREFERRED_GRANDFATHERED[]  = {
00086        "jbo",               "tlh",               "lb",
00087        "nv",                "nb",                "nn",                
00088        NULL
00089 };
00090 
00091 /*returns TRUE if a is an ID separator FALSE otherwise*/
00092 #define isIDSeparator(a) (a == '_' || a == '-')
00093 #define isKeywordSeparator(a) (a == '@' )
00094 #define isEndOfTag(a) (a == '\0' )
00095 
00096 #define isPrefixLetter(a) ((a=='x')||(a=='X')||(a=='i')||(a=='I'))
00097 
00098 /*returns TRUE if one of the special prefixes is here (s=string)
00099   'x-' or 'i-' */
00100 #define isIDPrefix(s) (isPrefixLetter(s[0])&&isIDSeparator(s[1]))
00101 #define isKeywordPrefix(s) ( isKeywordSeparator(s[0]) )
00102 
00103 /* Dot terminates it because of POSIX form  where dot precedes the codepage
00104  * except for variant */
00105 #define isTerminator(a)  ((a==0)||(a=='.')||(a=='@'))
00106 
00107 /* {{{ return the offset of 'key' in the array 'list'.
00108  * returns -1 if not present */
00109 static int16_t findOffset(const char* const* list, const char* key)
00110 {
00111        const char* const* anchor = list;
00112        while (*list != NULL) {
00113               if (strcmp(key, *list) == 0) {
00114                      return (int16_t)(list - anchor);
00115               }
00116               list++;
00117        }
00118 
00119        return -1;
00120 
00121 }
00122 /*}}}*/
00123 
00124 static char* getPreferredTag(char* gf_tag)
00125 { 
00126        char* result = NULL;
00127        int grOffset = 0;
00128 
00129        grOffset = findOffset( LOC_GRANDFATHERED ,gf_tag);
00130        if( grOffset < LOC_PREFERRED_GRANDFATHERED_LEN ){
00131               /* return preferred tag */
00132               result = estrdup( LOC_PREFERRED_GRANDFATHERED[grOffset] );
00133        } else {
00134               /* Return correct grandfathered language tag */
00135               result = estrdup( LOC_GRANDFATHERED[grOffset] );
00136        }
00137        return result;
00138 }
00139 
00140 /* {{{
00141 * returns the position of next token for lookup 
00142 * or -1 if no token
00143 * strtokr equivalent search for token in reverse direction 
00144 */
00145 static int getStrrtokenPos(char* str, int savedPos)
00146 {
00147        int result =-1;
00148        int i;
00149        
00150        for(i=savedPos-1; i>=0; i--) {
00151               if(isIDSeparator(*(str+i)) ){
00152                      /* delimiter found; check for singleton */
00153                      if(i>=2 && isIDSeparator(*(str+i-2)) ){
00154                             /* a singleton; so send the position of token before the singleton */
00155                             result = i-2;
00156                      } else {
00157                             result = i;
00158                      }
00159                      break;
00160               }
00161        }
00162        if(result < 1){
00163               /* Just in case inavlid locale e.g. '-x-xyz' or '-sl_Latn' */
00164               result =-1;
00165        }
00166        return result;
00167 }
00168 /* }}} */
00169 
00170 /* {{{
00171 * returns the position of a singleton if present 
00172 * returns -1 if no singleton
00173 * strtok equivalent search for singleton
00174 */
00175 static int getSingletonPos(char* str)
00176 {
00177        int result =-1;
00178        int i=0;
00179        int len = 0;
00180        
00181        if( str && ((len=strlen(str))>0) ){
00182               for( i=0; i<len ; i++){
00183                      if( isIDSeparator(*(str+i)) ){
00184                             if( i==1){
00185                                    /* string is of the form x-avy or a-prv1 */
00186                                    result =0;
00187                                    break;
00188                             } else {
00189                                    /* delimiter found; check for singleton */
00190                                    if( isIDSeparator(*(str+i+2)) ){
00191                                           /* a singleton; so send the position of separator before singleton */
00192                                           result = i+1;
00193                                           break;
00194                                    }
00195                             }
00196                      }
00197               }/* end of for */
00198               
00199        }
00200        return result;
00201 }
00202 /* }}} */
00203 
00204 /* {{{ proto static string Locale::getDefault(  )
00205    Get default locale */
00206 /* }}} */
00207 /* {{{ proto static string locale_get_default( )
00208    Get default locale */
00209 PHP_NAMED_FUNCTION(zif_locale_get_default)
00210 {
00211        if( INTL_G(default_locale) == NULL ) {
00212               INTL_G(default_locale) = pestrdup( uloc_getDefault(), 1);
00213        }
00214        RETURN_STRING( INTL_G(default_locale), TRUE );
00215 }
00216 
00217 /* }}} */
00218 
00219 /* {{{ proto static string Locale::setDefault( string $locale )
00220    Set default locale */
00221 /* }}} */
00222 /* {{{ proto static string locale_set_default( string $locale )
00223    Set default locale */
00224 PHP_NAMED_FUNCTION(zif_locale_set_default)
00225 {
00226        char* locale_name = NULL;
00227        int   len=0;  
00228 
00229        if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC,  "s",
00230               &locale_name ,&len ) == FAILURE)
00231        {
00232               intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
00233                             "locale_set_default: unable to parse input params", 0 TSRMLS_CC );
00234 
00235               RETURN_FALSE;
00236        }
00237 
00238        if(len == 0) {
00239               locale_name =  (char *)uloc_getDefault() ;
00240               len = strlen(locale_name);
00241        }
00242 
00243        zend_alter_ini_entry(LOCALE_INI_NAME, sizeof(LOCALE_INI_NAME), locale_name, len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME); 
00244 
00245        RETURN_TRUE;
00246 }
00247 /* }}} */
00248 
00249 /* {{{
00250 * Gets the value from ICU 
00251 * common code shared by get_primary_language,get_script or get_region or get_variant
00252 * result = 0 if error, 1 if successful , -1 if no value
00253 */
00254 static char* get_icu_value_internal( char* loc_name , char* tag_name, int* result , int fromParseLocale)
00255 {
00256        char*         tag_value     = NULL;
00257        int32_t       tag_value_len   = 512;
00258 
00259        int           singletonPos         = 0;
00260        char*         mod_loc_name  = NULL;
00261        int           grOffset      = 0;
00262 
00263        int32_t       buflen          = 512;
00264        UErrorCode    status          = U_ZERO_ERROR;
00265 
00266 
00267        if( strcmp(tag_name, LOC_CANONICALIZE_TAG) != 0 ){
00268               /* Handle  grandfathered languages */
00269               grOffset =  findOffset( LOC_GRANDFATHERED , loc_name );
00270               if( grOffset >= 0 ){
00271                      if( strcmp(tag_name , LOC_LANG_TAG)==0 ){
00272                             tag_value = estrdup(loc_name);
00273                             return tag_value;
00274                      } else {
00275                             /* Since Grandfathered , no value , do nothing , retutn NULL */
00276                             return NULL;
00277                      }
00278               }
00279 
00280        if( fromParseLocale==1 ){
00281               /* Handle singletons */
00282               if( strcmp(tag_name , LOC_LANG_TAG)==0 ){
00283                      if( strlen(loc_name)>1 && (isIDPrefix(loc_name) ==1 ) ){
00284                             return loc_name;
00285                      }
00286               }
00287 
00288               singletonPos = getSingletonPos( loc_name );      
00289               if( singletonPos == 0){
00290                      /* singleton at start of script, region , variant etc.
00291                       * or invalid singleton at start of language */
00292                      return NULL;
00293               } else if( singletonPos > 0 ){
00294                      /* singleton at some position except at start
00295                       * strip off the singleton and rest of the loc_name */
00296                      mod_loc_name = estrndup ( loc_name , singletonPos-1);
00297               }
00298        } /* end of if fromParse */
00299 
00300        } /* end of if != LOC_CANONICAL_TAG */
00301 
00302        if( mod_loc_name == NULL){
00303               mod_loc_name = estrdup(loc_name ); 
00304        }
00305 
00306        /* Proceed to ICU */
00307     do{
00308               tag_value = erealloc( tag_value , buflen  );
00309               tag_value_len = buflen;
00310 
00311               if( strcmp(tag_name , LOC_SCRIPT_TAG)==0 ){
00312                      buflen = uloc_getScript ( mod_loc_name ,tag_value , tag_value_len , &status);
00313               }
00314               if( strcmp(tag_name , LOC_LANG_TAG )==0 ){
00315                      buflen = uloc_getLanguage ( mod_loc_name ,tag_value , tag_value_len , &status);
00316               }
00317               if( strcmp(tag_name , LOC_REGION_TAG)==0 ){
00318                      buflen = uloc_getCountry ( mod_loc_name ,tag_value , tag_value_len , &status);
00319               }
00320               if( strcmp(tag_name , LOC_VARIANT_TAG)==0 ){
00321                      buflen = uloc_getVariant ( mod_loc_name ,tag_value , tag_value_len , &status);
00322               }
00323               if( strcmp(tag_name , LOC_CANONICALIZE_TAG)==0 ){
00324                      buflen = uloc_canonicalize ( mod_loc_name ,tag_value , tag_value_len , &status);
00325               }
00326 
00327               if( U_FAILURE( status ) ) {
00328                      if( status == U_BUFFER_OVERFLOW_ERROR ) {
00329                             status = U_ZERO_ERROR;
00330                             continue;
00331                      }
00332 
00333                      /* Error in retriving data */
00334                      *result = 0;
00335                      if( tag_value ){
00336                             efree( tag_value );
00337                      }
00338                      if( mod_loc_name ){
00339                             efree( mod_loc_name);
00340                      }
00341                      return NULL;
00342               }
00343        } while( buflen > tag_value_len );
00344 
00345        if(  buflen ==0 ){
00346               /* No value found */
00347               *result = -1;
00348               if( tag_value ){
00349                      efree( tag_value );
00350               }
00351               if( mod_loc_name ){
00352                      efree( mod_loc_name);
00353               }
00354               return NULL;
00355        } else {
00356               *result = 1;
00357        }
00358 
00359        if( mod_loc_name ){
00360               efree( mod_loc_name);
00361        }
00362        return tag_value;
00363 }
00364 /* }}} */
00365 
00366 /* {{{
00367 * Gets the value from ICU , called when PHP userspace function is called
00368 * common code shared by get_primary_language,get_script or get_region or get_variant
00369 */
00370 static void get_icu_value_src_php( char* tag_name, INTERNAL_FUNCTION_PARAMETERS) 
00371 {
00372 
00373        char*       loc_name               = NULL;
00374        int         loc_name_len           = 0;
00375 
00376        char*       tag_value              = NULL;
00377        char*       empty_result    = "";
00378 
00379        int         result                 = 0;
00380        char*       msg                    = NULL;
00381 
00382        UErrorCode  status                 = U_ZERO_ERROR;
00383 
00384        intl_error_reset( NULL TSRMLS_CC );
00385 
00386        if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s",
00387        &loc_name ,&loc_name_len ) == FAILURE) {
00388               spprintf(&msg , 0, "locale_get_%s : unable to parse input params", tag_name );
00389               intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,  msg , 1 TSRMLS_CC );
00390               efree(msg);
00391 
00392               RETURN_FALSE;
00393     }
00394 
00395        if(loc_name_len == 0) {
00396               loc_name = INTL_G(default_locale);
00397        }
00398 
00399        /* Call ICU get */
00400        tag_value = get_icu_value_internal( loc_name , tag_name , &result ,0);
00401 
00402        /* No value found */
00403        if( result == -1 ) {
00404               if( tag_value){
00405                      efree( tag_value);
00406               }
00407               RETURN_STRING( empty_result , TRUE);
00408        }
00409 
00410        /* value found */
00411        if( tag_value){
00412               RETURN_STRING( tag_value , FALSE);
00413        }
00414 
00415        /* Error encountered while fetching the value */
00416        if( result ==0) {
00417               spprintf(&msg , 0, "locale_get_%s : unable to get locale %s", tag_name , tag_name );
00418               intl_error_set( NULL, status, msg , 1 TSRMLS_CC );
00419               efree(msg);
00420               RETURN_NULL();
00421        }
00422 
00423 }
00424 /* }}} */
00425 
00426 /* {{{ proto static string Locale::getScript($locale) 
00427  * gets the script for the $locale 
00428  }}} */
00429 /* {{{ proto static string locale_get_script($locale) 
00430  * gets the script for the $locale 
00431  */
00432 PHP_FUNCTION( locale_get_script ) 
00433 {
00434        get_icu_value_src_php( LOC_SCRIPT_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
00435 }
00436 /* }}} */
00437 
00438 /* {{{ proto static string Locale::getRegion($locale) 
00439  * gets the region for the $locale 
00440  }}} */
00441 /* {{{ proto static string locale_get_region($locale) 
00442  * gets the region for the $locale 
00443  */
00444 PHP_FUNCTION( locale_get_region ) 
00445 {
00446        get_icu_value_src_php( LOC_REGION_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
00447 }
00448 /* }}} */
00449 
00450 /* {{{ proto static string Locale::getPrimaryLanguage($locale) 
00451  * gets the primary language for the $locale 
00452  }}} */
00453 /* {{{ proto static string locale_get_primary_language($locale) 
00454  * gets the primary language for the $locale 
00455  */
00456 PHP_FUNCTION(locale_get_primary_language ) 
00457 {
00458        get_icu_value_src_php( LOC_LANG_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
00459 }
00460 /* }}} */
00461 
00462 
00463 /* {{{
00464  * common code shared by display_xyz functions to  get the value from ICU 
00465  }}} */
00466 static void get_icu_disp_value_src_php( char* tag_name, INTERNAL_FUNCTION_PARAMETERS) 
00467 {
00468        char*       loc_name               = NULL;
00469        int         loc_name_len           = 0;
00470 
00471        char*       disp_loc_name       = NULL;
00472        int         disp_loc_name_len   = 0;
00473        int         free_loc_name       = 0;
00474 
00475        UChar*      disp_name       = NULL;
00476        int32_t     disp_name_len   = 0;
00477 
00478        char*       mod_loc_name        = NULL;
00479 
00480        int32_t     buflen                 = 512;
00481        UErrorCode  status                 = U_ZERO_ERROR;
00482 
00483        char*       utf8value              = NULL;
00484        int         utf8value_len          = 0;
00485 
00486        char*       msg                    = NULL;
00487        int         grOffset        = 0;
00488 
00489        intl_error_reset( NULL TSRMLS_CC );
00490 
00491        if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s|s",
00492               &loc_name, &loc_name_len , 
00493               &disp_loc_name ,&disp_loc_name_len ) == FAILURE)
00494        {
00495               spprintf(&msg , 0, "locale_get_display_%s : unable to parse input params", tag_name );
00496               intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,  msg , 1 TSRMLS_CC );
00497               efree(msg);
00498               RETURN_FALSE;
00499        }
00500 
00501        if(loc_name_len == 0) {
00502        loc_name = INTL_G(default_locale);
00503        }
00504 
00505        if( strcmp(tag_name, DISP_NAME) != 0 ){
00506               /* Handle grandfathered languages */
00507               grOffset = findOffset( LOC_GRANDFATHERED , loc_name );
00508               if( grOffset >= 0 ){
00509                      if( strcmp(tag_name , LOC_LANG_TAG)==0 ){
00510                             mod_loc_name = getPreferredTag( loc_name );
00511                      } else {
00512                             /* Since Grandfathered, no value, do nothing, retutn NULL */
00513                             RETURN_FALSE;
00514                      }
00515               }
00516        } /* end of if != LOC_CANONICAL_TAG */
00517 
00518        if( mod_loc_name==NULL ){
00519               mod_loc_name = estrdup( loc_name );
00520        }
00521        
00522        /* Check if disp_loc_name passed , if not use default locale */
00523        if( !disp_loc_name){
00524               disp_loc_name = estrdup(INTL_G(default_locale));
00525               free_loc_name = 1;
00526        }
00527 
00528     /* Get the disp_value for the given locale */
00529     do{
00530         disp_name = erealloc( disp_name , buflen  );
00531         disp_name_len = buflen;
00532 
00533               if( strcmp(tag_name , LOC_LANG_TAG)==0 ){
00534                      buflen = uloc_getDisplayLanguage ( mod_loc_name , disp_loc_name , disp_name , disp_name_len , &status);
00535               } else if( strcmp(tag_name , LOC_SCRIPT_TAG)==0 ){
00536                      buflen = uloc_getDisplayScript ( mod_loc_name , disp_loc_name , disp_name , disp_name_len , &status);
00537               } else if( strcmp(tag_name , LOC_REGION_TAG)==0 ){
00538                      buflen = uloc_getDisplayCountry ( mod_loc_name , disp_loc_name , disp_name , disp_name_len , &status);
00539               } else if( strcmp(tag_name , LOC_VARIANT_TAG)==0 ){
00540                      buflen = uloc_getDisplayVariant ( mod_loc_name , disp_loc_name , disp_name , disp_name_len , &status);
00541               } else if( strcmp(tag_name , DISP_NAME)==0 ){
00542                      buflen = uloc_getDisplayName ( mod_loc_name , disp_loc_name , disp_name , disp_name_len , &status);
00543               }
00544 
00545               if( U_FAILURE( status ) )
00546               {
00547                      if( status == U_BUFFER_OVERFLOW_ERROR )
00548                      {
00549                             status = U_ZERO_ERROR;
00550                             continue;
00551                      }
00552 
00553                      spprintf(&msg, 0, "locale_get_display_%s : unable to get locale %s", tag_name , tag_name );
00554                      intl_error_set( NULL, status, msg , 1 TSRMLS_CC );
00555                      efree(msg);
00556                      if( disp_name){
00557                             efree( disp_name );
00558                      }
00559                      if( mod_loc_name){
00560                             efree( mod_loc_name );
00561                      }
00562                      if (free_loc_name) {
00563                             efree(disp_loc_name);
00564                             disp_loc_name = NULL;
00565                      }
00566                      RETURN_FALSE;
00567               }
00568        } while( buflen > disp_name_len );
00569 
00570        if( mod_loc_name){
00571               efree( mod_loc_name );
00572        }
00573        if (free_loc_name) {
00574               efree(disp_loc_name);
00575               disp_loc_name = NULL;
00576        }
00577        /* Convert display locale name from UTF-16 to UTF-8. */
00578        intl_convert_utf16_to_utf8( &utf8value, &utf8value_len, disp_name, buflen, &status );
00579        efree( disp_name );
00580        if( U_FAILURE( status ) )
00581        {
00582               spprintf(&msg, 0, "locale_get_display_%s :error converting display name for %s to UTF-8", tag_name , tag_name );
00583               intl_error_set( NULL, status, msg , 1 TSRMLS_CC );
00584               efree(msg);
00585               RETURN_FALSE;
00586        }
00587 
00588        RETVAL_STRINGL( utf8value, utf8value_len , FALSE);
00589 
00590 }
00591 /* }}} */
00592 
00593 /* {{{ proto static string Locale::getDisplayName($locale[, $in_locale = null])
00594 * gets the name for the $locale in $in_locale or default_locale
00595  }}} */
00596 /* {{{ proto static string get_display_name($locale[, $in_locale = null])
00597 * gets the name for the $locale in $in_locale or default_locale
00598 */
00599 PHP_FUNCTION(locale_get_display_name) 
00600 {
00601     get_icu_disp_value_src_php( DISP_NAME , INTERNAL_FUNCTION_PARAM_PASSTHRU );
00602 }
00603 /* }}} */
00604 
00605 /* {{{ proto static string Locale::getDisplayLanguage($locale[, $in_locale = null])
00606 * gets the language for the $locale in $in_locale or default_locale
00607  }}} */
00608 /* {{{ proto static string get_display_language($locale[, $in_locale = null])
00609 * gets the language for the $locale in $in_locale or default_locale
00610 */
00611 PHP_FUNCTION(locale_get_display_language) 
00612 {
00613     get_icu_disp_value_src_php( LOC_LANG_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
00614 }
00615 /* }}} */
00616 
00617 /* {{{ proto static string Locale::getDisplayScript($locale, $in_locale = null)
00618 * gets the script for the $locale in $in_locale or default_locale
00619  }}} */
00620 /* {{{ proto static string get_display_script($locale, $in_locale = null)
00621 * gets the script for the $locale in $in_locale or default_locale
00622 */
00623 PHP_FUNCTION(locale_get_display_script) 
00624 {
00625     get_icu_disp_value_src_php( LOC_SCRIPT_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
00626 }
00627 /* }}} */
00628 
00629 /* {{{ proto static string Locale::getDisplayRegion($locale, $in_locale = null)
00630 * gets the region for the $locale in $in_locale or default_locale
00631  }}} */
00632 /* {{{ proto static string get_display_region($locale, $in_locale = null)
00633 * gets the region for the $locale in $in_locale or default_locale
00634 */
00635 PHP_FUNCTION(locale_get_display_region) 
00636 {
00637     get_icu_disp_value_src_php( LOC_REGION_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
00638 }
00639 /* }}} */
00640 
00641 /* {{{
00642 * proto static string Locale::getDisplayVariant($locale, $in_locale = null)
00643 * gets the variant for the $locale in $in_locale or default_locale
00644  }}} */
00645 /* {{{
00646 * proto static string get_display_variant($locale, $in_locale = null)
00647 * gets the variant for the $locale in $in_locale or default_locale
00648 */
00649 PHP_FUNCTION(locale_get_display_variant) 
00650 {
00651     get_icu_disp_value_src_php( LOC_VARIANT_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
00652 }
00653 /* }}} */
00654 
00655  /* {{{ proto static array getKeywords(string $locale) {
00656  * return an associative array containing keyword-value
00657  * pairs for this locale. The keys are keys to the array (doh!)
00658  * }}}*/
00659  /* {{{ proto static array locale_get_keywords(string $locale) {
00660  * return an associative array containing keyword-value
00661  * pairs for this locale. The keys are keys to the array (doh!)
00662  */ 
00663 PHP_FUNCTION( locale_get_keywords )
00664 {
00665     UEnumeration*   e        = NULL;
00666     UErrorCode      status   = U_ZERO_ERROR;
00667 
00668        const char*          kw_key        = NULL;
00669     int32_t         kw_key_len    = 0;
00670 
00671     char*            loc_name        = NULL;
00672     int                     loc_name_len    = 0;
00673 
00674 /* 
00675        ICU expects the buffer to be allocated  before calling the function 
00676        and so the buffer size has been explicitly specified 
00677        ICU uloc.h #define   ULOC_KEYWORD_AND_VALUES_CAPACITY   100 
00678        hence the kw_value buffer size is 100
00679 */
00680        char*         kw_value        = NULL;
00681     int32_t     kw_value_len    = 100;
00682 
00683     intl_error_reset( NULL TSRMLS_CC );
00684 
00685     if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s",
00686         &loc_name, &loc_name_len ) == FAILURE)
00687     {
00688         intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
00689              "locale_get_keywords: unable to parse input params", 0 TSRMLS_CC );
00690 
00691         RETURN_FALSE;
00692     }
00693 
00694     if(loc_name_len == 0) {
00695         loc_name = INTL_G(default_locale);
00696     }
00697 
00698        /* Get the keywords */
00699     e = uloc_openKeywords( loc_name, &status );
00700     if( e != NULL )
00701     {
00702               /* Traverse it, filling the return array. */
00703        array_init( return_value );
00704 
00705        while( ( kw_key = uenum_next( e, &kw_key_len, &status ) ) != NULL ){
00706                      kw_value = ecalloc( 1 , kw_value_len  );
00707 
00708                      /* Get the keyword value for each keyword */
00709                      kw_value_len=uloc_getKeywordValue( loc_name,kw_key, kw_value, kw_value_len ,  &status );
00710                      if (status == U_BUFFER_OVERFLOW_ERROR) {
00711                             status = U_ZERO_ERROR;
00712                             kw_value = erealloc( kw_value , kw_value_len+1);
00713                             kw_value_len=uloc_getKeywordValue( loc_name,kw_key, kw_value, kw_value_len+1 ,  &status );
00714                      } else if(!U_FAILURE(status)) {
00715                             kw_value = erealloc( kw_value , kw_value_len+1);
00716                      } 
00717                      if (U_FAILURE(status)) {
00718                      intl_error_set( NULL, FAILURE, "locale_get_keywords: Error encountered while getting the keyword  value for the  keyword", 0 TSRMLS_CC );
00719                             if( kw_value){
00720                                    efree( kw_value );
00721                             }
00722                             zval_dtor(return_value);
00723                      RETURN_FALSE;
00724                      }
00725 
00726                      add_assoc_stringl( return_value, (char *)kw_key, kw_value , kw_value_len, 0);
00727               } /* end of while */
00728 
00729        } /* end of if e!=NULL */
00730 
00731     uenum_close( e );
00732 }
00733 /* }}} */
00734 
00735  /* {{{ proto static string Locale::canonicalize($locale) 
00736  * @return string the canonicalized locale 
00737  * }}} */
00738  /* {{{ proto static string locale_canonicalize(Locale $loc, string $locale) 
00739  * @param string $locale    The locale string to canonicalize
00740  */
00741 PHP_FUNCTION(locale_canonicalize)
00742 {
00743        get_icu_value_src_php( LOC_CANONICALIZE_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
00744 }
00745 /* }}} */
00746 
00747 /* {{{ append_key_value 
00748 * Internal function which is called from locale_compose
00749 * gets the value for the key_name and appends to the loc_name
00750 * returns 1 if successful , -1 if not found , 
00751 * 0 if array element is not a string , -2 if buffer-overflow
00752 */
00753 static int append_key_value(smart_str* loc_name, HashTable* hash_arr, char* key_name)
00754 {
00755        zval** ele_value     = NULL;
00756 
00757        if(zend_hash_find(hash_arr , key_name , strlen(key_name) + 1 ,(void **)&ele_value ) == SUCCESS ) {
00758               if(Z_TYPE_PP(ele_value)!= IS_STRING ){
00759                      /* element value is not a string */
00760                      return FAILURE;
00761               }
00762               if(strcmp(key_name, LOC_LANG_TAG) != 0 && 
00763                  strcmp(key_name, LOC_GRANDFATHERED_LANG_TAG)!=0 ) {
00764                      /* not lang or grandfathered tag */
00765                      smart_str_appendl(loc_name, SEPARATOR , sizeof(SEPARATOR)-1);
00766               }
00767               smart_str_appendl(loc_name, Z_STRVAL_PP(ele_value) , Z_STRLEN_PP(ele_value));
00768               return SUCCESS;
00769        }
00770 
00771        return LOC_NOT_FOUND;
00772 }
00773 /* }}} */
00774 
00775 /* {{{ append_prefix , appends the prefix needed
00776 * e.g. private adds 'x'
00777 */
00778 static void add_prefix(smart_str* loc_name, char* key_name)
00779 {
00780        if( strncmp(key_name , LOC_PRIVATE_TAG , 7) == 0 ){
00781               smart_str_appendl(loc_name, SEPARATOR , sizeof(SEPARATOR)-1);
00782               smart_str_appendl(loc_name, PRIVATE_PREFIX , sizeof(PRIVATE_PREFIX)-1);
00783        }
00784 }
00785 /* }}} */
00786 
00787 /* {{{ append_multiple_key_values 
00788 * Internal function which is called from locale_compose
00789 * gets the multiple values for the key_name and appends to the loc_name
00790 * used for 'variant','extlang','private' 
00791 * returns 1 if successful , -1 if not found , 
00792 * 0 if array element is not a string , -2 if buffer-overflow
00793 */
00794 static int append_multiple_key_values(smart_str* loc_name, HashTable* hash_arr, char* key_name TSRMLS_DC)
00795 {
00796        zval** ele_value     = NULL;
00797        int    i             = 0;
00798        int    isFirstSubtag        = 0;
00799        int    max_value     = 0;
00800 
00801        /* Variant/ Extlang/Private etc. */
00802        if( zend_hash_find( hash_arr , key_name , strlen(key_name) + 1 ,(void **)&ele_value ) == SUCCESS ) {
00803               if( Z_TYPE_PP(ele_value) == IS_STRING ){
00804                      add_prefix( loc_name , key_name);
00805 
00806                      smart_str_appendl(loc_name, SEPARATOR , sizeof(SEPARATOR)-1);
00807                      smart_str_appendl(loc_name, Z_STRVAL_PP(ele_value) , Z_STRLEN_PP(ele_value));
00808                      return SUCCESS;
00809               } else if(Z_TYPE_PP(ele_value) == IS_ARRAY ) {
00810                      HashPosition pos;
00811                      HashTable *arr = HASH_OF(*ele_value);
00812                      zval **data = NULL;
00813 
00814                      zend_hash_internal_pointer_reset_ex(arr, &pos);
00815                      while(zend_hash_get_current_data_ex(arr, (void **)&data, &pos) != FAILURE) {
00816                             if(Z_TYPE_PP(data) != IS_STRING) {
00817                                    return FAILURE;
00818                             }
00819                             if (isFirstSubtag++ == 0){
00820                                    add_prefix(loc_name , key_name);
00821                             }
00822                             smart_str_appendl(loc_name, SEPARATOR , sizeof(SEPARATOR)-1);
00823                             smart_str_appendl(loc_name, Z_STRVAL_PP(data) , Z_STRLEN_PP(data));
00824                             zend_hash_move_forward_ex(arr, &pos);
00825                      }
00826                      return SUCCESS;
00827               } else {
00828                      return FAILURE;
00829               }
00830        } else {
00831               char cur_key_name[31];
00832               /* Decide the max_value: the max. no. of elements allowed */
00833               if( strcmp(key_name , LOC_VARIANT_TAG) ==0 ){
00834                      max_value  = MAX_NO_VARIANT;
00835               }
00836               if( strcmp(key_name , LOC_EXTLANG_TAG) ==0 ){
00837                      max_value  = MAX_NO_EXTLANG;
00838               }
00839               if( strcmp(key_name , LOC_PRIVATE_TAG) ==0 ){
00840                      max_value  = MAX_NO_PRIVATE;
00841               }
00842 
00843               /* Multiple variant values as variant0, variant1 ,variant2 */
00844               isFirstSubtag = 0;
00845               for( i=0 ; i< max_value; i++ ){  
00846                      snprintf( cur_key_name , 30, "%s%d", key_name , i);     
00847                      if( zend_hash_find( hash_arr , cur_key_name , strlen(cur_key_name) + 1,(void **)&ele_value ) == SUCCESS ){
00848                             if( Z_TYPE_PP(ele_value)!= IS_STRING ){
00849                                    /* variant is not a string */
00850                                    return FAILURE;
00851                             }
00852                             /* Add the contents */
00853                             if (isFirstSubtag++ == 0){
00854                                    add_prefix(loc_name , cur_key_name);
00855                             }
00856                             smart_str_appendl(loc_name, SEPARATOR , sizeof(SEPARATOR)-1);
00857                             smart_str_appendl(loc_name, Z_STRVAL_PP(ele_value) , Z_STRLEN_PP(ele_value));
00858                      }
00859               } /* end of for */
00860        } /* end of else */
00861 
00862        return SUCCESS;
00863 }
00864 /* }}} */
00865 
00866 /*{{{
00867 * If applicable sets error message and aborts locale_compose gracefully
00868 * returns 0  if locale_compose needs to be aborted 
00869 * otherwise returns 1
00870 */
00871 static int handleAppendResult( int result, smart_str* loc_name TSRMLS_DC)
00872 {
00873        intl_error_reset( NULL TSRMLS_CC );
00874        if( result == FAILURE) {
00875               intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
00876                       "locale_compose: parameter array element is not a string", 0 TSRMLS_CC );
00877               smart_str_free(loc_name);
00878               return 0;
00879        }
00880        return 1;
00881 }
00882 /* }}} */
00883 
00884 #define RETURN_SMART_STR(s) smart_str_0((s)); RETURN_STRINGL((s)->c, (s)->len, 0)
00885 /* {{{ proto static string Locale::composeLocale($array) 
00886 * Creates a locale by combining the parts of locale-ID passed  
00887 * }}} */
00888 /* {{{ proto static string compose_locale($array) 
00889 * Creates a locale by combining the parts of locale-ID passed  
00890 * }}} */
00891 PHP_FUNCTION(locale_compose)
00892 {
00893        smart_str            loc_name_s = {0};
00894        smart_str *loc_name = &loc_name_s;
00895        zval*                arr    = NULL;
00896        HashTable*           hash_arr = NULL;
00897        int                  result = 0;
00898 
00899        intl_error_reset( NULL TSRMLS_CC );
00900 
00901        if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "a",
00902               &arr) == FAILURE)
00903        {
00904               intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
00905                       "locale_compose: unable to parse input params", 0 TSRMLS_CC );
00906               RETURN_FALSE;
00907        }
00908 
00909        hash_arr = HASH_OF( arr );
00910 
00911        if( !hash_arr || zend_hash_num_elements( hash_arr ) == 0 )
00912               RETURN_FALSE;
00913 
00914        /* Check for grandfathered first */
00915        result = append_key_value(loc_name, hash_arr,  LOC_GRANDFATHERED_LANG_TAG);  
00916        if( result == SUCCESS){
00917               RETURN_SMART_STR(loc_name);
00918        }
00919        if( !handleAppendResult( result, loc_name TSRMLS_CC)){
00920               RETURN_FALSE;
00921        }
00922 
00923        /* Not grandfathered */
00924        result = append_key_value(loc_name, hash_arr , LOC_LANG_TAG);  
00925        if( result == LOC_NOT_FOUND ){
00926               intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
00927               "locale_compose: parameter array does not contain 'language' tag.", 0 TSRMLS_CC );
00928               smart_str_free(loc_name);
00929               RETURN_FALSE;
00930        }
00931        if( !handleAppendResult( result, loc_name TSRMLS_CC)){
00932               RETURN_FALSE;
00933        }
00934 
00935        /* Extlang */
00936        result = append_multiple_key_values(loc_name, hash_arr , LOC_EXTLANG_TAG TSRMLS_CC);
00937        if( !handleAppendResult( result, loc_name TSRMLS_CC)){
00938               RETURN_FALSE;
00939        }
00940 
00941        /* Script */
00942        result = append_key_value(loc_name, hash_arr , LOC_SCRIPT_TAG); 
00943        if( !handleAppendResult( result, loc_name TSRMLS_CC)){
00944               RETURN_FALSE;
00945        }
00946        
00947        /* Region */
00948        result = append_key_value( loc_name, hash_arr , LOC_REGION_TAG);
00949        if( !handleAppendResult( result, loc_name TSRMLS_CC)){
00950               RETURN_FALSE;
00951        }
00952 
00953        /* Variant */
00954        result = append_multiple_key_values( loc_name, hash_arr , LOC_VARIANT_TAG TSRMLS_CC); 
00955        if( !handleAppendResult( result, loc_name TSRMLS_CC)){
00956               RETURN_FALSE;
00957        }
00958 
00959        /* Private */
00960        result = append_multiple_key_values( loc_name, hash_arr , LOC_PRIVATE_TAG TSRMLS_CC);
00961        if( !handleAppendResult( result, loc_name TSRMLS_CC)){
00962               RETURN_FALSE;
00963        }
00964 
00965        RETURN_SMART_STR(loc_name);
00966 }
00967 /* }}} */
00968 
00969 
00970 /*{{{
00971 * Parses the locale and returns private subtags  if existing
00972 * else returns NULL
00973 * e.g. for locale='en_US-x-prv1-prv2-prv3'
00974 * returns a pointer to the string 'prv1-prv2-prv3'
00975 */
00976 static char* get_private_subtags(char* loc_name)
00977 {
00978        char*  result =NULL;
00979        int    singletonPos = 0;
00980        int    len =0; 
00981        char*  mod_loc_name =NULL;
00982 
00983        if( loc_name && (len = strlen(loc_name)>0 ) ){
00984               mod_loc_name = loc_name ; 
00985               len   = strlen(mod_loc_name);
00986               while( (singletonPos = getSingletonPos(mod_loc_name))!= -1){
00987 
00988                      if( singletonPos!=-1){ 
00989                             if( (*(mod_loc_name+singletonPos)=='x') || (*(mod_loc_name+singletonPos)=='X') ){          
00990                                    /* private subtag start found */
00991                                    if( singletonPos + 2 ==  len){
00992                                           /* loc_name ends with '-x-' ; return  NULL */
00993                                    }
00994                                    else{
00995                                           /* result = mod_loc_name + singletonPos +2; */
00996                                           result = estrndup(mod_loc_name + singletonPos+2  , (len -( singletonPos +2) ) );
00997                                    }
00998                                    break;
00999                             }
01000                             else{
01001                                    if( singletonPos + 1 >=  len){
01002                                           /* String end */
01003                                           break;
01004                                    } else {
01005                                           /* singleton found but not a private subtag , hence check further in the string for the private subtag */
01006                                           mod_loc_name = mod_loc_name + singletonPos +1;
01007                                           len = strlen(mod_loc_name);
01008                                    }
01009                             }
01010                      }
01011 
01012               } /* end of while */
01013        }
01014        
01015        return result;
01016 }
01017 /* }}} */
01018 
01019 /* {{{ code used by locale_parse
01020 */
01021 static int add_array_entry(char* loc_name, zval* hash_arr, char* key_name TSRMLS_DC)
01022 {
01023        char*   key_value    = NULL;
01024        char*   cur_key_name = NULL;
01025        char*   token               = NULL;
01026        char*   last_ptr     = NULL;
01027 
01028        int    result        = 0;
01029        int    cur_result    = 0;
01030        int    cnt           = 0;
01031 
01032 
01033        if( strcmp(key_name , LOC_PRIVATE_TAG)==0 ){
01034               key_value = get_private_subtags( loc_name );
01035               result = 1;
01036        } else {
01037               key_value = get_icu_value_internal( loc_name , key_name , &result,1 );
01038        }
01039        if( (strcmp(key_name , LOC_PRIVATE_TAG)==0) || 
01040               ( strcmp(key_name , LOC_VARIANT_TAG)==0) ){
01041               if( result > 0 && key_value){
01042                      /* Tokenize on the "_" or "-"  */
01043                      token = php_strtok_r( key_value , DELIMITER ,&last_ptr);       
01044                      if( cur_key_name ){
01045                             efree( cur_key_name);
01046                      }
01047                      cur_key_name = (char*)ecalloc( 25,  25);
01048                      sprintf( cur_key_name , "%s%d", key_name , cnt++);      
01049                      add_assoc_string( hash_arr, cur_key_name , token ,TRUE );
01050                      /* tokenize on the "_" or "-" and stop  at singleton if any */
01051                      while( (token = php_strtok_r(NULL , DELIMITER , &last_ptr)) && (strlen(token)>1) ){
01052                             sprintf( cur_key_name , "%s%d", key_name , cnt++);      
01053                             add_assoc_string( hash_arr, cur_key_name , token , TRUE );
01054                      }
01055 /*
01056                      if( strcmp(key_name, LOC_PRIVATE_TAG) == 0 ){
01057                      }
01058 */
01059               }
01060        } else {
01061               if( result == 1 ){
01062                      add_assoc_string( hash_arr, key_name , key_value , TRUE );
01063                      cur_result = 1;
01064               }
01065        }
01066 
01067        if( cur_key_name ){
01068               efree( cur_key_name);
01069        }
01070        /*if( key_name != LOC_PRIVATE_TAG && key_value){*/
01071        if( key_value){
01072               efree(key_value);    
01073        }
01074        return cur_result;
01075 }
01076 /* }}} */
01077 
01078 /* {{{ proto static array Locale::parseLocale($locale) 
01079 * parses a locale-id into an array the different parts of it
01080  }}} */
01081 /* {{{ proto static array parse_locale($locale) 
01082 * parses a locale-id into an array the different parts of it
01083 */
01084 PHP_FUNCTION(locale_parse)
01085 {
01086     char*       loc_name        = NULL;
01087     int         loc_name_len    = 0;
01088     int         grOffset           = 0;
01089 
01090     intl_error_reset( NULL TSRMLS_CC );
01091 
01092     if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s",
01093         &loc_name, &loc_name_len ) == FAILURE)
01094     {
01095         intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
01096              "locale_parse: unable to parse input params", 0 TSRMLS_CC );
01097 
01098         RETURN_FALSE;
01099     }
01100 
01101     if(loc_name_len == 0) {
01102         loc_name = INTL_G(default_locale);
01103     }
01104 
01105        array_init( return_value );
01106 
01107        grOffset =  findOffset( LOC_GRANDFATHERED , loc_name );
01108        if( grOffset >= 0 ){
01109               add_assoc_string( return_value , LOC_GRANDFATHERED_LANG_TAG , estrdup(loc_name) ,FALSE );
01110        }
01111        else{
01112               /* Not grandfathered */
01113               add_array_entry( loc_name , return_value , LOC_LANG_TAG TSRMLS_CC);
01114               add_array_entry( loc_name , return_value , LOC_SCRIPT_TAG TSRMLS_CC);
01115               add_array_entry( loc_name , return_value , LOC_REGION_TAG TSRMLS_CC);
01116               add_array_entry( loc_name , return_value , LOC_VARIANT_TAG TSRMLS_CC);
01117               add_array_entry( loc_name , return_value , LOC_PRIVATE_TAG TSRMLS_CC);
01118        }
01119 }
01120 /* }}} */
01121 
01122 /* {{{ proto static array Locale::getAllVariants($locale)
01123 * gets an array containing the list of variants, or null
01124  }}} */
01125 /* {{{ proto static array locale_get_all_variants($locale)
01126 * gets an array containing the list of variants, or null
01127 */
01128 PHP_FUNCTION(locale_get_all_variants)
01129 {
01130        char*         loc_name        = NULL;
01131        int           loc_name_len    = 0;
01132 
01133        int    result        = 0;
01134        char*  token         = NULL;
01135        char*  variant              = NULL;
01136        char*  saved_ptr     = NULL;
01137 
01138        intl_error_reset( NULL TSRMLS_CC );
01139        
01140        if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s",
01141        &loc_name, &loc_name_len ) == FAILURE)
01142        {
01143               intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
01144             "locale_parse: unable to parse input params", 0 TSRMLS_CC );
01145 
01146               RETURN_FALSE;
01147        }
01148 
01149        if(loc_name_len == 0) {
01150               loc_name = INTL_G(default_locale);
01151        }
01152 
01153 
01154        array_init( return_value );
01155 
01156        /* If the locale is grandfathered, stop, no variants */
01157        if( findOffset( LOC_GRANDFATHERED , loc_name ) >=  0 ){ 
01158               /* ("Grandfathered Tag. No variants."); */
01159        }
01160        else { 
01161        /* Call ICU variant */
01162               variant = get_icu_value_internal( loc_name , LOC_VARIANT_TAG , &result ,0);
01163               if( result > 0 && variant){
01164                      /* Tokenize on the "_" or "-" */
01165                      token = php_strtok_r( variant , DELIMITER , &saved_ptr);       
01166                      add_next_index_stringl( return_value, token , strlen(token) ,TRUE );
01167                      /* tokenize on the "_" or "-" and stop  at singleton if any    */
01168                      while( (token = php_strtok_r(NULL , DELIMITER, &saved_ptr)) && (strlen(token)>1) ){
01169                             add_next_index_stringl( return_value, token , strlen(token) ,TRUE );
01170                      }
01171               }
01172               if( variant ){
01173                      efree( variant );
01174               }
01175        }
01176                      
01177 
01178 }
01179 /* }}} */
01180 
01181 /*{{{
01182 * Converts to lower case and also replaces all hyphens with the underscore
01183 */
01184 static int strToMatch(char* str ,char *retstr)
01185 {
01186        char*  anchor        = NULL;
01187        char*  anchor1 = NULL;
01188        int    result        = 0;
01189        int    len    = 0;
01190 
01191     if( (!str) || str[0] == '\0'){
01192         return result;
01193     } else {
01194        anchor = retstr;
01195        anchor1 = str;
01196         len = strlen(str);
01197         while( (*str)!='\0' ){
01198               if( *str == '-' ){
01199                      *retstr =  '_';
01200               } else {
01201                      *retstr = tolower(*str);
01202               }
01203             str++;
01204             retstr++;
01205        }
01206        *retstr = '\0';
01207        retstr=  anchor;
01208        str=  anchor1;
01209        result = 1;
01210     }
01211 
01212     return(result);
01213 }
01214 /* }}} */
01215 
01216 /* {{{ proto static boolean Locale::filterMatches(string $langtag, string $locale[, bool $canonicalize])
01217 * Checks if a $langtag filter matches with $locale according to RFC 4647's basic filtering algorithm 
01218 */
01219 /* }}} */
01220 /* {{{ proto boolean locale_filter_matches(string $langtag, string $locale[, bool $canonicalize])
01221 * Checks if a $langtag filter matches with $locale according to RFC 4647's basic filtering algorithm 
01222 */
01223 PHP_FUNCTION(locale_filter_matches)
01224 {
01225        char*         lang_tag        = NULL;
01226        int           lang_tag_len    = 0;
01227        char*         loc_range       = NULL;
01228        int           loc_range_len   = 0;
01229 
01230        int           result        = 0;
01231        char*         token         = 0;
01232        char*         chrcheck      = NULL;
01233 
01234        char*         can_lang_tag    = NULL;
01235        char*         can_loc_range   = NULL;
01236 
01237        char*         cur_lang_tag    = NULL;
01238        char*         cur_loc_range   = NULL;
01239 
01240        zend_bool     boolCanonical        = 0;   
01241        UErrorCode    status        = U_ZERO_ERROR;
01242 
01243        intl_error_reset( NULL TSRMLS_CC );
01244        
01245        if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "ss|b",
01246               &lang_tag, &lang_tag_len , &loc_range , &loc_range_len , 
01247               &boolCanonical) == FAILURE)
01248        {
01249               intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
01250               "locale_filter_matches: unable to parse input params", 0 TSRMLS_CC );
01251 
01252               RETURN_FALSE;
01253        }
01254 
01255        if(loc_range_len == 0) {
01256               loc_range = INTL_G(default_locale);
01257        }
01258 
01259        if( strcmp(loc_range,"*")==0){
01260               RETURN_TRUE;
01261        }
01262 
01263        if( boolCanonical ){
01264               /* canonicalize loc_range */
01265               can_loc_range=get_icu_value_internal( loc_range , LOC_CANONICALIZE_TAG , &result , 0);
01266               if( result ==0) {
01267                      intl_error_set( NULL, status, 
01268                             "locale_filter_matches : unable to canonicalize loc_range" , 0 TSRMLS_CC );
01269                      RETURN_FALSE;
01270               }
01271 
01272               /* canonicalize lang_tag */
01273               can_lang_tag = get_icu_value_internal( lang_tag , LOC_CANONICALIZE_TAG , &result ,  0);
01274               if( result ==0) {
01275                      intl_error_set( NULL, status, 
01276                             "locale_filter_matches : unable to canonicalize lang_tag" , 0 TSRMLS_CC );
01277                      RETURN_FALSE;
01278               }
01279 
01280               /* Convert to lower case for case-insensitive comparison */
01281               cur_lang_tag = ecalloc( 1, strlen(can_lang_tag) + 1);
01282 
01283               /* Convert to lower case for case-insensitive comparison */
01284               result = strToMatch( can_lang_tag , cur_lang_tag);
01285               if( result == 0) {
01286                      efree( cur_lang_tag );
01287                      efree( can_lang_tag );
01288                      RETURN_FALSE;
01289               }
01290 
01291               cur_loc_range = ecalloc( 1, strlen(can_loc_range) + 1);
01292               result = strToMatch( can_loc_range , cur_loc_range );
01293               if( result == 0) {
01294                      efree( cur_lang_tag );
01295                      efree( can_lang_tag );
01296                      efree( cur_loc_range );
01297                      efree( can_loc_range );
01298                      RETURN_FALSE;
01299               }
01300 
01301               /* check if prefix */
01302               token  = strstr( cur_lang_tag , cur_loc_range );
01303        
01304               if( token && (token==cur_lang_tag) ){
01305                      /* check if the char. after match is SEPARATOR */
01306                      chrcheck = token + (strlen(cur_loc_range));
01307                      if( isIDSeparator(*chrcheck) || isEndOfTag(*chrcheck) ){ 
01308                             if( cur_lang_tag){
01309                                    efree( cur_lang_tag );
01310                             }
01311                             if( cur_loc_range){
01312                                    efree( cur_loc_range );
01313                             }
01314                             if( can_lang_tag){
01315                                    efree( can_lang_tag );
01316                             }
01317                             if( can_loc_range){
01318                                    efree( can_loc_range );
01319                             }
01320                             RETURN_TRUE;
01321                      }
01322               }
01323 
01324               /* No prefix as loc_range */
01325               if( cur_lang_tag){
01326                      efree( cur_lang_tag );
01327               }
01328               if( cur_loc_range){
01329                      efree( cur_loc_range );
01330               }
01331               if( can_lang_tag){
01332                      efree( can_lang_tag );
01333               }
01334               if( can_loc_range){
01335                      efree( can_loc_range );
01336               }
01337               RETURN_FALSE;
01338 
01339        } /* end of if isCanonical */
01340        else{
01341               /* Convert to lower case for case-insensitive comparison */
01342               cur_lang_tag = ecalloc( 1, strlen(lang_tag ) + 1);
01343               
01344               result = strToMatch( lang_tag , cur_lang_tag);
01345               if( result == 0) {
01346                      efree( cur_lang_tag );
01347                      RETURN_FALSE;
01348               }
01349               cur_loc_range = ecalloc( 1, strlen(loc_range ) + 1);
01350               result = strToMatch( loc_range , cur_loc_range );
01351               if( result == 0) {
01352                      efree( cur_lang_tag );
01353                      efree( cur_loc_range );
01354                      RETURN_FALSE;
01355               }
01356 
01357               /* check if prefix */
01358               token  = strstr( cur_lang_tag , cur_loc_range );
01359               
01360               if( token && (token==cur_lang_tag) ){
01361                      /* check if the char. after match is SEPARATOR */
01362                      chrcheck = token + (strlen(cur_loc_range));
01363                      if( isIDSeparator(*chrcheck) || isEndOfTag(*chrcheck) ){ 
01364                             if( cur_lang_tag){
01365                                    efree( cur_lang_tag );
01366                             }
01367                             if( cur_loc_range){
01368                                    efree( cur_loc_range );
01369                             }
01370                             RETURN_TRUE;
01371                      }
01372               }
01373 
01374               /* No prefix as loc_range */
01375               if( cur_lang_tag){
01376                      efree( cur_lang_tag );
01377               }
01378               if( cur_loc_range){
01379                      efree( cur_loc_range );
01380               }
01381               RETURN_FALSE;
01382 
01383        }
01384 }
01385 /* }}} */
01386 
01387 static void array_cleanup( char* arr[] , int arr_size)
01388 {
01389        int i=0;
01390        for( i=0; i< arr_size; i++ ){ 
01391               if( arr[i*2] ){
01392                      efree( arr[i*2]);
01393               }
01394        }
01395        efree(arr);
01396 }
01397 
01398 #define LOOKUP_CLEAN_RETURN(value) array_cleanup(cur_arr, cur_arr_len); return (value)
01399 /* {{{
01400 * returns the lookup result to lookup_loc_range_src_php 
01401 * internal function
01402 */
01403 static char* lookup_loc_range(char* loc_range, HashTable* hash_arr, int canonicalize  TSRMLS_DC)
01404 {
01405        int    i = 0;
01406        int    cur_arr_len = 0;
01407        int result = 0;
01408 
01409        char* lang_tag = NULL;
01410        zval** ele_value = NULL;
01411        char** cur_arr = NULL;
01412 
01413        char* cur_loc_range  = NULL;
01414        char* can_loc_range  = NULL;
01415        int    saved_pos = 0;
01416 
01417        char* return_value = NULL;
01418 
01419        cur_arr = ecalloc(zend_hash_num_elements(hash_arr)*2, sizeof(char *));
01420        /* convert the array to lowercase , also replace hyphens with the underscore and store it in cur_arr */
01421        for(zend_hash_internal_pointer_reset(hash_arr);
01422               zend_hash_has_more_elements(hash_arr) == SUCCESS;
01423               zend_hash_move_forward(hash_arr)) {
01424               
01425               if (zend_hash_get_current_data(hash_arr, (void**)&ele_value) == FAILURE) {
01426                      /* Should never actually fail since the key is known to exist.*/
01427                      continue;
01428               }
01429               if(Z_TYPE_PP(ele_value)!= IS_STRING) {
01430                      /* element value is not a string */
01431                      intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: locale array element is not a string", 0 TSRMLS_CC);
01432                      LOOKUP_CLEAN_RETURN(NULL);
01433               } 
01434               cur_arr[cur_arr_len*2] = estrndup(Z_STRVAL_PP(ele_value), Z_STRLEN_PP(ele_value));
01435               result = strToMatch(Z_STRVAL_PP(ele_value), cur_arr[cur_arr_len*2]);
01436               if(result == 0) {
01437                      intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: unable to canonicalize lang_tag", 0 TSRMLS_CC);
01438                      LOOKUP_CLEAN_RETURN(NULL);
01439               }
01440               cur_arr[cur_arr_len*2+1] = Z_STRVAL_PP(ele_value);
01441               cur_arr_len++ ; 
01442        } /* end of for */
01443 
01444        /* Canonicalize array elements */
01445        if(canonicalize) {
01446               for(i=0; i<cur_arr_len; i++) { 
01447                      lang_tag = get_icu_value_internal(cur_arr[i*2], LOC_CANONICALIZE_TAG, &result, 0);
01448                      if(result != 1 || lang_tag == NULL || !lang_tag[0]) {
01449                             if(lang_tag) {
01450                                    efree(lang_tag);
01451                             }
01452                             intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: unable to canonicalize lang_tag" , 0 TSRMLS_CC);
01453                             LOOKUP_CLEAN_RETURN(NULL);
01454                      }
01455                      cur_arr[i*2] = erealloc(cur_arr[i*2], strlen(lang_tag)+1);
01456                      result = strToMatch(lang_tag, cur_arr[i*2]);     
01457                      efree(lang_tag);
01458                      if(result == 0) {
01459                             intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: unable to canonicalize lang_tag" , 0 TSRMLS_CC);
01460                             LOOKUP_CLEAN_RETURN(NULL);
01461                      }
01462               }
01463 
01464        }
01465 
01466        if(canonicalize) {
01467               /* Canonicalize the loc_range */
01468               can_loc_range = get_icu_value_internal(loc_range, LOC_CANONICALIZE_TAG, &result , 0);
01469               if( result != 1 || can_loc_range == NULL || !can_loc_range[0]) {
01470                      /* Error */
01471                      intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: unable to canonicalize loc_range" , 0 TSRMLS_CC );
01472                      if(can_loc_range) {
01473                             efree(can_loc_range);
01474                      }
01475                      LOOKUP_CLEAN_RETURN(NULL);
01476               } else {
01477                      loc_range = can_loc_range;
01478               }
01479        } 
01480 
01481        cur_loc_range = ecalloc(1, strlen(loc_range)+1);
01482        /* convert to lower and replace hyphens */
01483        result = strToMatch(loc_range, cur_loc_range);   
01484        if(can_loc_range) {
01485               efree(can_loc_range);
01486        }
01487        if(result == 0) {
01488               intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: unable to canonicalize lang_tag" , 0 TSRMLS_CC);
01489               LOOKUP_CLEAN_RETURN(NULL);
01490        }
01491 
01492        /* Lookup for the lang_tag match */
01493        saved_pos = strlen(cur_loc_range);
01494        while(saved_pos > 0) {
01495               for(i=0; i< cur_arr_len; i++){ 
01496                      if(cur_arr[i*2] != NULL && strlen(cur_arr[i*2]) == saved_pos && strncmp(cur_loc_range, cur_arr[i*2], saved_pos) == 0) {       
01497                             /* Match found */
01498                             return_value = estrdup(canonicalize?cur_arr[i*2]:cur_arr[i*2+1]);
01499                             efree(cur_loc_range);
01500                             LOOKUP_CLEAN_RETURN(return_value);
01501                      }
01502               }
01503               saved_pos = getStrrtokenPos(cur_loc_range, saved_pos);
01504        }
01505 
01506        /* Match not found */
01507        efree(cur_loc_range);
01508        LOOKUP_CLEAN_RETURN(NULL);
01509 }
01510 /* }}} */
01511 
01512 /* {{{ proto string Locale::lookup(array $langtag, string $locale[, bool $canonicalize[, string $default = null]]) 
01513 * Searchs the items in $langtag for the best match to the language
01514 * range 
01515 */
01516 /* }}} */
01517 /* {{{ proto string locale_lookup(array $langtag, string $locale[, bool $canonicalize[, string $default = null]])
01518 * Searchs the items in $langtag for the best match to the language
01519 * range 
01520 */
01521 PHP_FUNCTION(locale_lookup)
01522 {
01523        char*         fallback_loc                = NULL;
01524        int           fallback_loc_len     = 0;
01525        char*         loc_range                   = NULL;
01526        int           loc_range_len               = 0;
01527 
01528        zval*         arr                         = NULL;
01529        HashTable*    hash_arr             = NULL;
01530        zend_bool     boolCanonical = 0;
01531        char*         result               =NULL;
01532 
01533        intl_error_reset( NULL TSRMLS_CC );
01534 
01535        if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "as|bs", &arr, &loc_range, &loc_range_len,
01536               &boolCanonical,      &fallback_loc, &fallback_loc_len) == FAILURE) {
01537               intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,  "locale_lookup: unable to parse input params", 0 TSRMLS_CC );
01538               RETURN_FALSE;
01539        }
01540 
01541        if(loc_range_len == 0) {
01542               loc_range = INTL_G(default_locale);
01543        }
01544 
01545        hash_arr = HASH_OF(arr);
01546 
01547        if( !hash_arr || zend_hash_num_elements( hash_arr ) == 0 ) {
01548               RETURN_EMPTY_STRING();
01549        } 
01550        
01551        result = lookup_loc_range(loc_range, hash_arr, boolCanonical TSRMLS_CC);
01552        if(result == NULL || result[0] == '\0') {
01553               if( fallback_loc ) {
01554                      result = estrndup(fallback_loc, fallback_loc_len);
01555               } else {
01556                      RETURN_EMPTY_STRING();
01557               }
01558        }
01559 
01560        RETVAL_STRINGL(result, strlen(result), 0);
01561 }
01562 /* }}} */
01563 
01564 /* {{{ proto string Locale::acceptFromHttp(string $http_accept)
01565 * Tries to find out best available locale based on HTTP “Accept-Language” header
01566 */
01567 /* }}} */
01568 /* {{{ proto string locale_accept_from_http(string $http_accept)
01569 * Tries to find out best available locale based on HTTP “Accept-Language” header
01570 */
01571 PHP_FUNCTION(locale_accept_from_http)
01572 {
01573        UEnumeration *available;
01574        char *http_accept = NULL;
01575        int http_accept_len;
01576        UErrorCode status = 0;
01577        int len;
01578        char resultLocale[INTL_MAX_LOCALE_LEN+1];
01579        UAcceptResult outResult;
01580 
01581        if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s", &http_accept, &http_accept_len) == FAILURE)
01582        {
01583               intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
01584               "locale_accept_from_http: unable to parse input parameters", 0 TSRMLS_CC );
01585               RETURN_FALSE;
01586        }
01587        
01588        available = ures_openAvailableLocales(NULL, &status);
01589        INTL_CHECK_STATUS(status, "locale_accept_from_http: failed to retrieve locale list");
01590        len = uloc_acceptLanguageFromHTTP(resultLocale, INTL_MAX_LOCALE_LEN, 
01591                                           &outResult, http_accept, available, &status);
01592        uenum_close(available);
01593        INTL_CHECK_STATUS(status, "locale_accept_from_http: failed to find acceptable locale");
01594        if (len < 0 || outResult == ULOC_ACCEPT_FAILED) {
01595               RETURN_FALSE;
01596        }
01597        RETURN_STRINGL(resultLocale, len, 1);
01598 }
01599 /* }}} */
01600 
01601 /*
01602  * Local variables:
01603  * tab-width: 4
01604  * c-basic-offset: 4
01605  * End:
01606  * vim600: noet sw=4 ts=4 fdm=marker
01607  * vim<600: noet sw=4 ts=4
01608  *can_loc_len
01609 */