PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LCOV - code coverage report
Current view: top level - ext/intl/locale - locale_methods.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 612 701 87.3 %
Date: 2022-01-21 Functions: 34 34 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :    +----------------------------------------------------------------------+
       3             :    | PHP Version 7                                                        |
       4             :    +----------------------------------------------------------------------+
       5             :    | This source file is subject to version 3.01 of the PHP license,      |
       6             :    | that is bundled with this package in the file LICENSE, and is        |
       7             :    | available through the world-wide-web at the following url:           |
       8             :    | http://www.php.net/license/3_01.txt                                  |
       9             :    | If you did not receive a copy of the PHP license and are unable to   |
      10             :    | obtain it through the world-wide-web, please send a note to          |
      11             :    | license@php.net so we can mail you a copy immediately.               |
      12             :    +----------------------------------------------------------------------+
      13             :    | Authors: Kirti Velankar <kirtig@yahoo-inc.com>                       |
      14             :    +----------------------------------------------------------------------+
      15             : */
      16             : 
      17             : /* $Id$ */
      18             : 
      19             : #ifdef HAVE_CONFIG_H
      20             : #include "config.h"
      21             : #endif
      22             : 
      23             : #include <unicode/ustring.h>
      24             : #include <unicode/udata.h>
      25             : #include <unicode/putil.h>
      26             : #include <unicode/ures.h>
      27             : 
      28             : #include "php_intl.h"
      29             : #include "locale.h"
      30             : #include "locale_class.h"
      31             : #include "locale_methods.h"
      32             : #include "intl_convert.h"
      33             : #include "intl_data.h"
      34             : 
      35             : #include <zend_API.h>
      36             : #include <zend.h>
      37             : #include <php.h>
      38             : #include "main/php_ini.h"
      39             : #include "zend_smart_str.h"
      40             : 
      41             : ZEND_EXTERN_MODULE_GLOBALS( intl )
      42             : 
      43             : /* Sizes required for the strings "variant15" , "extlang11", "private12" etc. */
      44             : #define SEPARATOR "_"
      45             : #define SEPARATOR1 "-"
      46             : #define DELIMITER "-_"
      47             : #define EXTLANG_PREFIX "a"
      48             : #define PRIVATE_PREFIX "x"
      49             : #define DISP_NAME "name"
      50             : 
      51             : #define MAX_NO_VARIANT  15
      52             : #define MAX_NO_EXTLANG  3
      53             : #define MAX_NO_PRIVATE  15
      54             : #define MAX_NO_LOOKUP_LANG_TAG  100
      55             : 
      56             : #define LOC_NOT_FOUND 1
      57             : 
      58             : /* Sizes required for the strings "variant15" , "extlang3", "private12" etc. */
      59             : #define VARIANT_KEYNAME_LEN  11
      60             : #define EXTLANG_KEYNAME_LEN  10
      61             : #define PRIVATE_KEYNAME_LEN  11
      62             : 
      63             : /* Based on IANA registry at the time of writing this code
      64             : *
      65             : */
      66             : static const char * const LOC_GRANDFATHERED[] = {
      67             :         "art-lojban",         "i-klingon",          "i-lux",                      "i-navajo",           "no-bok",             "no-nyn",
      68             :         "cel-gaulish",                "en-GB-oed",          "i-ami",
      69             :         "i-bnn",              "i-default",          "i-enochian",
      70             :         "i-mingo",            "i-pwn",              "i-tao",
      71             :         "i-tay",              "i-tsu",              "sgn-BE-fr",
      72             :         "sgn-BE-nl",          "sgn-CH-de",          "zh-cmn",
      73             :         "zh-cmn-Hans",                "zh-cmn-Hant",                "zh-gan" ,
      74             :         "zh-guoyu",           "zh-hakka",           "zh-min",
      75             :         "zh-min-nan",                 "zh-wuu",             "zh-xiang",
      76             :         "zh-yue",             NULL
      77             : };
      78             : 
      79             : /* Based on IANA registry at the time of writing this code
      80             : *  This array lists the preferred values for the grandfathered tags if applicable
      81             : *  This is in sync with the array LOC_GRANDFATHERED
      82             : *  e.g. the offsets of the grandfathered tags match the offset of the preferred  value
      83             : */
      84             : static const int                LOC_PREFERRED_GRANDFATHERED_LEN = 6;
      85             : static const char * const       LOC_PREFERRED_GRANDFATHERED[]  = {
      86             :         "jbo",                        "tlh",                        "lb",
      87             :         "nv",                         "nb",                 "nn",
      88             :         NULL
      89             : };
      90             : 
      91             : /*returns TRUE if a is an ID separator FALSE otherwise*/
      92             : #define isIDSeparator(a) (a == '_' || a == '-')
      93             : #define isKeywordSeparator(a) (a == '@' )
      94             : #define isEndOfTag(a) (a == '\0' )
      95             : 
      96             : #define isPrefixLetter(a) ((a=='x')||(a=='X')||(a=='i')||(a=='I'))
      97             : 
      98             : /*returns TRUE if one of the special prefixes is here (s=string)
      99             :   'x-' or 'i-' */
     100             : #define isIDPrefix(s) (isPrefixLetter(s[0])&&isIDSeparator(s[1]))
     101             : #define isKeywordPrefix(s) ( isKeywordSeparator(s[0]) )
     102             : 
     103             : /* Dot terminates it because of POSIX form  where dot precedes the codepage
     104             :  * except for variant */
     105             : #define isTerminator(a)  ((a==0)||(a=='.')||(a=='@'))
     106             : 
     107             : /* {{{ return the offset of 'key' in the array 'list'.
     108             :  * returns -1 if not present */
     109        1478 : static int16_t findOffset(const char* const* list, const char* key)
     110             : {
     111        1478 :         const char* const* anchor = list;
     112       47312 :         while (*list != NULL) {
     113       44456 :                 if (strcmp(key, *list) == 0) {
     114         100 :                         return (int16_t)(list - anchor);
     115             :                 }
     116       44356 :                 list++;
     117             :         }
     118             : 
     119        1378 :         return -1;
     120             : 
     121             : }
     122             : /*}}}*/
     123             : 
     124          18 : static char* getPreferredTag(const char* gf_tag)
     125             : {
     126          18 :         char* result = NULL;
     127          18 :         zend_off_t grOffset = 0;
     128             : 
     129          18 :         grOffset = findOffset( LOC_GRANDFATHERED ,gf_tag);
     130          18 :         if(grOffset < 0) {
     131           0 :                 return NULL;
     132             :         }
     133          18 :         if( grOffset < LOC_PREFERRED_GRANDFATHERED_LEN ){
     134             :                 /* return preferred tag */
     135           6 :                 result = estrdup( LOC_PREFERRED_GRANDFATHERED[grOffset] );
     136             :         } else {
     137             :                 /* Return correct grandfathered language tag */
     138          12 :                 result = estrdup( LOC_GRANDFATHERED[grOffset] );
     139             :         }
     140          18 :         return result;
     141             : }
     142             : 
     143             : /* {{{
     144             : * returns the position of next token for lookup
     145             : * or -1 if no token
     146             : * strtokr equivalent search for token in reverse direction
     147             : */
     148           9 : static zend_off_t getStrrtokenPos(char* str, zend_off_t savedPos)
     149             : {
     150           9 :         zend_off_t result =-1;
     151             :         zend_off_t i;
     152             : 
     153          42 :         for(i=savedPos-1; i>=0; i--) {
     154          40 :                 if(isIDSeparator(*(str+i)) ){
     155             :                         /* delimiter found; check for singleton */
     156           7 :                         if(i>=2 && isIDSeparator(*(str+i-2)) ){
     157             :                                 /* a singleton; so send the position of token before the singleton */
     158           0 :                                 result = i-2;
     159             :                         } else {
     160           7 :                                 result = i;
     161             :                         }
     162           7 :                         break;
     163             :                 }
     164             :         }
     165           9 :         if(result < 1){
     166             :                 /* Just in case inavlid locale e.g. '-x-xyz' or '-sl_Latn' */
     167           2 :                 result =-1;
     168             :         }
     169           9 :         return result;
     170             : }
     171             : /* }}} */
     172             : 
     173             : /* {{{
     174             : * returns the position of a singleton if present
     175             : * returns -1 if no singleton
     176             : * strtok equivalent search for singleton
     177             : */
     178         354 : static zend_off_t getSingletonPos(const char* str)
     179             : {
     180         354 :         zend_off_t result =-1;
     181         354 :         zend_off_t i=0;
     182         354 :         size_t len = 0;
     183             : 
     184         354 :         if( str && ((len=strlen(str))>0) ){
     185        3170 :                 for( i=0; (size_t)i < len ; i++){
     186        2912 :                         if( isIDSeparator(*(str+i)) ){
     187         492 :                                 if( i==1){
     188             :                                         /* string is of the form x-avy or a-prv1 */
     189          18 :                                         result =0;
     190          18 :                                         break;
     191             :                                 } else {
     192             :                                         /* delimiter found; check for singleton */
     193         474 :                                         if( isIDSeparator(*(str+i+2)) ){
     194             :                                                 /* a singleton; so send the position of separator before singleton */
     195          78 :                                                 result = i+1;
     196          78 :                                                 break;
     197             :                                         }
     198             :                                 }
     199             :                         }
     200             :                 }/* end of for */
     201             : 
     202             :         }
     203         354 :         return result;
     204             : }
     205             : /* }}} */
     206             : 
     207             : /* {{{ proto static string Locale::getDefault(  )
     208             :    Get default locale */
     209             : /* }}} */
     210             : /* {{{ proto static string locale_get_default( )
     211             :    Get default locale */
     212          79 : PHP_NAMED_FUNCTION(zif_locale_get_default)
     213             : {
     214         158 :         RETURN_STRING( intl_locale_get_default(  ) );
     215             : }
     216             : 
     217             : /* }}} */
     218             : 
     219             : /* {{{ proto static string Locale::setDefault( string $locale )
     220             :    Set default locale */
     221             : /* }}} */
     222             : /* {{{ proto static string locale_set_default( string $locale )
     223             :    Set default locale */
     224          75 : PHP_NAMED_FUNCTION(zif_locale_set_default)
     225             : {
     226             :         zend_string* locale_name;
     227             :         zend_string *ini_name;
     228          75 :         char *default_locale = NULL;
     229             : 
     230          75 :         if(zend_parse_parameters( ZEND_NUM_ARGS(),  "S", &locale_name) == FAILURE)
     231             :         {
     232           1 :                 intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
     233             :                                 "locale_set_default: unable to parse input params", 0 );
     234             : 
     235           1 :                 RETURN_FALSE;
     236             :         }
     237             : 
     238          74 :         if (ZSTR_LEN(locale_name) == 0) {
     239           0 :                 default_locale = (char *)uloc_getDefault();
     240           0 :                 locale_name = zend_string_init(default_locale, strlen(default_locale), 0);
     241             :         }
     242             : 
     243          74 :         ini_name = zend_string_init(LOCALE_INI_NAME, sizeof(LOCALE_INI_NAME) - 1, 0);
     244          74 :         zend_alter_ini_entry(ini_name, locale_name, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
     245             :         zend_string_release(ini_name);
     246          74 :         if (default_locale != NULL) {
     247           0 :                 zend_string_release(locale_name);
     248             :         }
     249             : 
     250          74 :         RETURN_TRUE;
     251             : }
     252             : /* }}} */
     253             : 
     254             : /* {{{
     255             : * Gets the value from ICU
     256             : * common code shared by get_primary_language,get_script or get_region or get_variant
     257             : * result = 0 if error, 1 if successful , -1 if no value
     258             : */
     259        1479 : static zend_string* get_icu_value_internal( const char* loc_name , char* tag_name, int* result , int fromParseLocale)
     260             : {
     261        1479 :         zend_string* tag_value      = NULL;
     262        1479 :         int32_t      tag_value_len  = 512;
     263             : 
     264        1479 :         zend_off_t   singletonPos   = 0;
     265        1479 :         char*        mod_loc_name   = NULL;
     266        1479 :         zend_off_t   grOffset       = 0;
     267             : 
     268        1479 :         int32_t      buflen         = 512;
     269        1479 :         UErrorCode   status         = U_ZERO_ERROR;
     270             : 
     271        1479 :         if (strlen(loc_name) > INTL_MAX_LOCALE_LEN) {
     272           0 :                 return NULL;
     273             :         }
     274             : 
     275        1479 :         if( strcmp(tag_name, LOC_CANONICALIZE_TAG) != 0 ){
     276             :                 /* Handle  grandfathered languages */
     277         496 :                 grOffset =  findOffset( LOC_GRANDFATHERED , loc_name );
     278         496 :                 if( grOffset >= 0 ){
     279          12 :                         if( strcmp(tag_name , LOC_LANG_TAG)==0 ){
     280           8 :                                 return zend_string_init(loc_name, strlen(loc_name), 0);
     281             :                         } else {
     282             :                                 /* Since Grandfathered , no value , do nothing , retutn NULL */
     283           8 :                                 return NULL;
     284             :                         }
     285             :                 }
     286             : 
     287         484 :         if( fromParseLocale==1 ){
     288             :                 /* Handle singletons */
     289         272 :                 if( strcmp(tag_name , LOC_LANG_TAG)==0 ){
     290          68 :                         if( strlen(loc_name)>1 && (isIDPrefix(loc_name) == 1) ){
     291           4 :                                 return zend_string_init(loc_name, strlen(loc_name), 0);
     292             :                         }
     293             :                 }
     294             : 
     295         270 :                 singletonPos = getSingletonPos( loc_name );
     296         270 :                 if( singletonPos == 0){
     297             :                         /* singleton at start of script, region , variant etc.
     298             :                          * or invalid singleton at start of language */
     299          14 :                         return NULL;
     300         256 :                 } else if( singletonPos > 0 ){
     301             :                         /* singleton at some position except at start
     302             :                          * strip off the singleton and rest of the loc_name */
     303          56 :                         mod_loc_name = estrndup ( loc_name , singletonPos-1);
     304             :                 }
     305             :         } /* end of if fromParse */
     306             : 
     307             :         } /* end of if != LOC_CANONICAL_TAG */
     308             : 
     309        1451 :         if( mod_loc_name == NULL){
     310        1395 :                 mod_loc_name = estrdup(loc_name );
     311             :         }
     312             : 
     313             :         /* Proceed to ICU */
     314             :         do{
     315        1451 :                 if (tag_value) {
     316           0 :                         tag_value = zend_string_realloc( tag_value , buflen, 0);
     317             :                 } else {
     318        2902 :                         tag_value = zend_string_alloc( buflen, 0);
     319             :                 }
     320        1451 :                 tag_value_len = buflen;
     321             : 
     322        1451 :                 if( strcmp(tag_name , LOC_SCRIPT_TAG)==0 ){
     323         132 :                         buflen = uloc_getScript ( mod_loc_name , tag_value->val , tag_value_len , &status);
     324             :                 }
     325        1451 :                 if( strcmp(tag_name , LOC_LANG_TAG )==0 ){
     326         130 :                         buflen = uloc_getLanguage ( mod_loc_name , tag_value->val , tag_value_len , &status);
     327             :                 }
     328        1451 :                 if( strcmp(tag_name , LOC_REGION_TAG)==0 ){
     329         132 :                         buflen = uloc_getCountry ( mod_loc_name , tag_value->val , tag_value_len , &status);
     330             :                 }
     331        1451 :                 if( strcmp(tag_name , LOC_VARIANT_TAG)==0 ){
     332          74 :                         buflen = uloc_getVariant ( mod_loc_name , tag_value->val , tag_value_len , &status);
     333             :                 }
     334        1451 :                 if( strcmp(tag_name , LOC_CANONICALIZE_TAG)==0 ){
     335         983 :                         buflen = uloc_canonicalize ( mod_loc_name , tag_value->val , tag_value_len , &status);
     336             :                 }
     337             : 
     338        1451 :                 if( U_FAILURE( status ) ) {
     339           0 :                         if( status == U_BUFFER_OVERFLOW_ERROR ) {
     340           0 :                                 status = U_ZERO_ERROR;
     341           0 :                                 buflen++; /* add space for \0 */
     342           0 :                                 continue;
     343             :                         }
     344             : 
     345             :                         /* Error in retriving data */
     346           0 :                         *result = 0;
     347           0 :                         if( tag_value ){
     348             :                                 zend_string_release( tag_value );
     349             :                         }
     350           0 :                         if( mod_loc_name ){
     351           0 :                                 efree( mod_loc_name);
     352             :                         }
     353           0 :                         return NULL;
     354             :                 }
     355        1451 :         } while( buflen > tag_value_len );
     356             : 
     357        1451 :         if(  buflen ==0 ){
     358             :                 /* No value found */
     359         198 :                 *result = -1;
     360         198 :                 if( tag_value ){
     361             :                         zend_string_release( tag_value );
     362             :                 }
     363         198 :                 if( mod_loc_name ){
     364         198 :                         efree( mod_loc_name);
     365             :                 }
     366         198 :                 return NULL;
     367             :         } else {
     368        1253 :                 *result = 1;
     369             :         }
     370             : 
     371        1253 :         if( mod_loc_name ){
     372        1253 :                 efree( mod_loc_name);
     373             :         }
     374             : 
     375        1253 :         tag_value->len = strlen(tag_value->val);
     376        1253 :         return tag_value;
     377             : }
     378             : /* }}} */
     379             : 
     380             : /* {{{
     381             : * Gets the value from ICU , called when PHP userspace function is called
     382             : * common code shared by get_primary_language,get_script or get_region or get_variant
     383             : */
     384         613 : static void get_icu_value_src_php( char* tag_name, INTERNAL_FUNCTION_PARAMETERS)
     385             : {
     386             : 
     387         613 :         const char* loc_name            = NULL;
     388         613 :         size_t         loc_name_len     = 0;
     389             : 
     390         613 :         zend_string*   tag_value                = NULL;
     391         613 :         char*       empty_result        = "";
     392             : 
     393         613 :         int         result              = 0;
     394         613 :         char*       msg                 = NULL;
     395             : 
     396         613 :         UErrorCode  status              = U_ZERO_ERROR;
     397             : 
     398         613 :         intl_error_reset( NULL );
     399             : 
     400         613 :         if(zend_parse_parameters( ZEND_NUM_ARGS(), "s",
     401             :         &loc_name ,&loc_name_len ) == FAILURE) {
     402           4 :                 spprintf(&msg , 0, "locale_get_%s : unable to parse input params", tag_name );
     403           4 :                 intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,  msg , 1 );
     404           4 :                 efree(msg);
     405             : 
     406         617 :                 RETURN_FALSE;
     407             :     }
     408             : 
     409         609 :         if(loc_name_len == 0) {
     410           0 :                 loc_name = intl_locale_get_default();
     411           0 :                 loc_name_len = strlen(loc_name);
     412             :         }
     413             : 
     414         609 :         INTL_CHECK_LOCALE_LEN(loc_name_len);
     415             : 
     416             :         /* Call ICU get */
     417         608 :         tag_value = get_icu_value_internal( loc_name , tag_name , &result ,0);
     418             : 
     419             :         /* No value found */
     420         608 :         if( result == -1 ) {
     421          78 :                 if( tag_value){
     422             :                         zend_string_release( tag_value);
     423             :                 }
     424         156 :                 RETURN_STRING( empty_result);
     425             :         }
     426             : 
     427             :         /* value found */
     428         530 :         if( tag_value){
     429         522 :                 RETVAL_STR( tag_value );
     430         522 :                 return;
     431             :         }
     432             : 
     433             :         /* Error encountered while fetching the value */
     434           8 :         if( result ==0) {
     435           8 :                 spprintf(&msg , 0, "locale_get_%s : unable to get locale %s", tag_name , tag_name );
     436           8 :                 intl_error_set( NULL, status, msg , 1 );
     437           8 :                 efree(msg);
     438           8 :                 RETURN_NULL();
     439             :         }
     440             : 
     441             : }
     442             : /* }}} */
     443             : 
     444             : /* {{{ proto static string Locale::getScript($locale)
     445             :  * gets the script for the $locale
     446             :  }}} */
     447             : /* {{{ proto static string locale_get_script($locale)
     448             :  * gets the script for the $locale
     449             :  */
     450          73 : PHP_FUNCTION( locale_get_script )
     451             : {
     452          73 :         get_icu_value_src_php( LOC_SCRIPT_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
     453          73 : }
     454             : /* }}} */
     455             : 
     456             : /* {{{ proto static string Locale::getRegion($locale)
     457             :  * gets the region for the $locale
     458             :  }}} */
     459             : /* {{{ proto static string locale_get_region($locale)
     460             :  * gets the region for the $locale
     461             :  */
     462          73 : PHP_FUNCTION( locale_get_region )
     463             : {
     464          73 :         get_icu_value_src_php( LOC_REGION_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
     465          73 : }
     466             : /* }}} */
     467             : 
     468             : /* {{{ proto static string Locale::getPrimaryLanguage($locale)
     469             :  * gets the primary language for the $locale
     470             :  }}} */
     471             : /* {{{ proto static string locale_get_primary_language($locale)
     472             :  * gets the primary language for the $locale
     473             :  */
     474          72 : PHP_FUNCTION(locale_get_primary_language )
     475             : {
     476          72 :         get_icu_value_src_php( LOC_LANG_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
     477          72 : }
     478             : /* }}} */
     479             : 
     480             : 
     481             : /* {{{
     482             :  * common code shared by display_xyz functions to  get the value from ICU
     483             :  }}} */
     484        1162 : static void get_icu_disp_value_src_php( char* tag_name, INTERNAL_FUNCTION_PARAMETERS)
     485             : {
     486        1162 :         const char* loc_name            = NULL;
     487        1162 :         size_t         loc_name_len     = 0;
     488             : 
     489        1162 :         const char* disp_loc_name       = NULL;
     490        1162 :         size_t      disp_loc_name_len   = 0;
     491        1162 :         int         free_loc_name       = 0;
     492             : 
     493        1162 :         UChar*      disp_name           = NULL;
     494        1162 :         int32_t     disp_name_len       = 0;
     495             : 
     496        1162 :         char*       mod_loc_name        = NULL;
     497             : 
     498        1162 :         int32_t     buflen              = 512;
     499        1162 :         UErrorCode  status              = U_ZERO_ERROR;
     500             : 
     501             :         zend_string* u8str;
     502             : 
     503        1162 :         char*       msg                 = NULL;
     504        1162 :         int         grOffset            = 0;
     505             : 
     506        1162 :         intl_error_reset( NULL );
     507             : 
     508        1162 :         if(zend_parse_parameters( ZEND_NUM_ARGS(), "s|s",
     509             :                 &loc_name, &loc_name_len ,
     510             :                 &disp_loc_name ,&disp_loc_name_len ) == FAILURE)
     511             :         {
     512           5 :                 spprintf(&msg , 0, "locale_get_display_%s : unable to parse input params", tag_name );
     513           5 :                 intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,  msg , 1 );
     514           5 :                 efree(msg);
     515          57 :                 RETURN_FALSE;
     516             :         }
     517             : 
     518        1157 :     if(loc_name_len > ULOC_FULLNAME_CAPACITY) {
     519             :         /* See bug 67397: overlong locale names cause trouble in uloc_getDisplayName */
     520           5 :                 spprintf(&msg , 0, "locale_get_display_%s : name too long", tag_name );
     521           5 :                 intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,  msg , 1 );
     522           5 :                 efree(msg);
     523           5 :                 RETURN_FALSE;
     524             :     }
     525             : 
     526        1152 :         if(loc_name_len == 0) {
     527           0 :                 loc_name = intl_locale_get_default();
     528             :         }
     529             : 
     530        1152 :         if( strcmp(tag_name, DISP_NAME) != 0 ){
     531             :                 /* Handle grandfathered languages */
     532         870 :                 grOffset = findOffset( LOC_GRANDFATHERED , loc_name );
     533         870 :                 if( grOffset >= 0 ){
     534          54 :                         if( strcmp(tag_name , LOC_LANG_TAG)==0 ){
     535          18 :                                 mod_loc_name = getPreferredTag( loc_name );
     536             :                         } else {
     537             :                                 /* Since Grandfathered, no value, do nothing, retutn NULL */
     538          36 :                                 RETURN_FALSE;
     539             :                         }
     540             :                 }
     541             :         } /* end of if != LOC_CANONICAL_TAG */
     542             : 
     543        1116 :         if( mod_loc_name==NULL ){
     544        1098 :                 mod_loc_name = estrdup( loc_name );
     545             :         }
     546             : 
     547             :         /* Check if disp_loc_name passed , if not use default locale */
     548        1116 :         if( !disp_loc_name){
     549           0 :                 disp_loc_name = estrdup(intl_locale_get_default());
     550           0 :                 free_loc_name = 1;
     551             :         }
     552             : 
     553             :     /* Get the disp_value for the given locale */
     554             :     do{
     555        1116 :         disp_name = erealloc( disp_name , buflen * sizeof(UChar)  );
     556        1116 :         disp_name_len = buflen;
     557             : 
     558        1116 :                 if( strcmp(tag_name , LOC_LANG_TAG)==0 ){
     559         222 :                         buflen = uloc_getDisplayLanguage ( mod_loc_name , disp_loc_name , disp_name , disp_name_len , &status);
     560         894 :                 } else if( strcmp(tag_name , LOC_SCRIPT_TAG)==0 ){
     561         204 :                         buflen = uloc_getDisplayScript ( mod_loc_name , disp_loc_name , disp_name , disp_name_len , &status);
     562         690 :                 } else if( strcmp(tag_name , LOC_REGION_TAG)==0 ){
     563         204 :                         buflen = uloc_getDisplayCountry ( mod_loc_name , disp_loc_name , disp_name , disp_name_len , &status);
     564         486 :                 } else if( strcmp(tag_name , LOC_VARIANT_TAG)==0 ){
     565         204 :                         buflen = uloc_getDisplayVariant ( mod_loc_name , disp_loc_name , disp_name , disp_name_len , &status);
     566         282 :                 } else if( strcmp(tag_name , DISP_NAME)==0 ){
     567         282 :                         buflen = uloc_getDisplayName ( mod_loc_name , disp_loc_name , disp_name , disp_name_len , &status);
     568             :                 }
     569             : 
     570             :                 /* U_STRING_NOT_TERMINATED_WARNING is admissible here; don't look for it */
     571        1116 :                 if( U_FAILURE( status ) )
     572             :                 {
     573           6 :                         if( status == U_BUFFER_OVERFLOW_ERROR )
     574             :                         {
     575           0 :                                 status = U_ZERO_ERROR;
     576           0 :                                 continue;
     577             :                         }
     578             : 
     579           6 :                         spprintf(&msg, 0, "locale_get_display_%s : unable to get locale %s", tag_name , tag_name );
     580           6 :                         intl_error_set( NULL, status, msg , 1 );
     581           6 :                         efree(msg);
     582           6 :                         if( disp_name){
     583           6 :                                 efree( disp_name );
     584             :                         }
     585           6 :                         if( mod_loc_name){
     586           6 :                                 efree( mod_loc_name );
     587             :                         }
     588           6 :                         if (free_loc_name) {
     589           0 :                                 efree((void *)disp_loc_name);
     590           0 :                                 disp_loc_name = NULL;
     591             :                         }
     592           6 :                         RETURN_FALSE;
     593             :                 }
     594        1110 :         } while( buflen > disp_name_len );
     595             : 
     596        1110 :         if( mod_loc_name){
     597        1110 :                 efree( mod_loc_name );
     598             :         }
     599        1110 :         if (free_loc_name) {
     600           0 :                 efree((void *)disp_loc_name);
     601           0 :                 disp_loc_name = NULL;
     602             :         }
     603             :         /* Convert display locale name from UTF-16 to UTF-8. */
     604        1110 :         u8str = intl_convert_utf16_to_utf8(disp_name, buflen, &status );
     605        1110 :         efree( disp_name );
     606        1110 :         if( !u8str )
     607             :         {
     608           0 :                 spprintf(&msg, 0, "locale_get_display_%s :error converting display name for %s to UTF-8", tag_name , tag_name );
     609           0 :                 intl_error_set( NULL, status, msg , 1 );
     610           0 :                 efree(msg);
     611           0 :                 RETURN_FALSE;
     612             :         }
     613             : 
     614        1110 :         RETVAL_NEW_STR( u8str );
     615             : }
     616             : /* }}} */
     617             : 
     618             : /* {{{ proto static string Locale::getDisplayName($locale[, $in_locale = null])
     619             : * gets the name for the $locale in $in_locale or default_locale
     620             :  }}} */
     621             : /* {{{ proto static string get_display_name($locale[, $in_locale = null])
     622             : * gets the name for the $locale in $in_locale or default_locale
     623             : */
     624         288 : PHP_FUNCTION(locale_get_display_name)
     625             : {
     626         288 :     get_icu_disp_value_src_php( DISP_NAME , INTERNAL_FUNCTION_PARAM_PASSTHRU );
     627         288 : }
     628             : /* }}} */
     629             : 
     630             : /* {{{ proto static string Locale::getDisplayLanguage($locale[, $in_locale = null])
     631             : * gets the language for the $locale in $in_locale or default_locale
     632             :  }}} */
     633             : /* {{{ proto static string get_display_language($locale[, $in_locale = null])
     634             : * gets the language for the $locale in $in_locale or default_locale
     635             : */
     636         223 : PHP_FUNCTION(locale_get_display_language)
     637             : {
     638         223 :     get_icu_disp_value_src_php( LOC_LANG_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
     639         223 : }
     640             : /* }}} */
     641             : 
     642             : /* {{{ proto static string Locale::getDisplayScript($locale, $in_locale = null)
     643             : * gets the script for the $locale in $in_locale or default_locale
     644             :  }}} */
     645             : /* {{{ proto static string get_display_script($locale, $in_locale = null)
     646             : * gets the script for the $locale in $in_locale or default_locale
     647             : */
     648         217 : PHP_FUNCTION(locale_get_display_script)
     649             : {
     650         217 :     get_icu_disp_value_src_php( LOC_SCRIPT_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
     651         217 : }
     652             : /* }}} */
     653             : 
     654             : /* {{{ proto static string Locale::getDisplayRegion($locale, $in_locale = null)
     655             : * gets the region for the $locale in $in_locale or default_locale
     656             :  }}} */
     657             : /* {{{ proto static string get_display_region($locale, $in_locale = null)
     658             : * gets the region for the $locale in $in_locale or default_locale
     659             : */
     660         217 : PHP_FUNCTION(locale_get_display_region)
     661             : {
     662         217 :     get_icu_disp_value_src_php( LOC_REGION_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
     663         217 : }
     664             : /* }}} */
     665             : 
     666             : /* {{{
     667             : * proto static string Locale::getDisplayVariant($locale, $in_locale = null)
     668             : * gets the variant for the $locale in $in_locale or default_locale
     669             :  }}} */
     670             : /* {{{
     671             : * proto static string get_display_variant($locale, $in_locale = null)
     672             : * gets the variant for the $locale in $in_locale or default_locale
     673             : */
     674         217 : PHP_FUNCTION(locale_get_display_variant)
     675             : {
     676         217 :     get_icu_disp_value_src_php( LOC_VARIANT_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
     677         217 : }
     678             : /* }}} */
     679             : 
     680             :  /* {{{ proto static array getKeywords(string $locale) {
     681             :  * return an associative array containing keyword-value
     682             :  * pairs for this locale. The keys are keys to the array (doh!)
     683             :  * }}}*/
     684             :  /* {{{ proto static array locale_get_keywords(string $locale) {
     685             :  * return an associative array containing keyword-value
     686             :  * pairs for this locale. The keys are keys to the array (doh!)
     687             :  */
     688          75 : PHP_FUNCTION( locale_get_keywords )
     689             : {
     690          75 :     UEnumeration*   e        = NULL;
     691          75 :     UErrorCode      status   = U_ZERO_ERROR;
     692             : 
     693          75 :     const char*         kw_key        = NULL;
     694          75 :     int32_t         kw_key_len    = 0;
     695             : 
     696          75 :     const char*         loc_name        = NULL;
     697          75 :     size_t                      loc_name_len    = 0;
     698             : 
     699             : /*
     700             :         ICU expects the buffer to be allocated  before calling the function
     701             :         and so the buffer size has been explicitly specified
     702             :         ICU uloc.h #define      ULOC_KEYWORD_AND_VALUES_CAPACITY   100
     703             :         hence the kw_value buffer size is 100
     704             : */
     705             :         zend_string *kw_value_str;
     706          75 :     int32_t     kw_value_len = 100;
     707             : 
     708          75 :     intl_error_reset( NULL );
     709             : 
     710          75 :     if(zend_parse_parameters( ZEND_NUM_ARGS(), "s",
     711             :         &loc_name, &loc_name_len ) == FAILURE)
     712             :     {
     713           1 :         intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
     714             :              "locale_get_keywords: unable to parse input params", 0 );
     715             : 
     716           2 :         RETURN_FALSE;
     717             :     }
     718             : 
     719          74 :         INTL_CHECK_LOCALE_LEN(strlen(loc_name));
     720             : 
     721          74 :     if(loc_name_len == 0) {
     722           0 :         loc_name = intl_locale_get_default();
     723             :     }
     724             : 
     725             :         /* Get the keywords */
     726          74 :     e = uloc_openKeywords( loc_name, &status );
     727          74 :     if( e != NULL )
     728             :     {
     729             :                 /* Traverse it, filling the return array. */
     730          24 :         array_init( return_value );
     731             : 
     732          84 :         while( ( kw_key = uenum_next( e, &kw_key_len, &status ) ) != NULL ){
     733          36 :                 kw_value_len = 100;
     734          72 :                         kw_value_str = zend_string_alloc(kw_value_len, 0);
     735             : 
     736             :                         /* Get the keyword value for each keyword */
     737          36 :                         kw_value_len=uloc_getKeywordValue( loc_name, kw_key, ZSTR_VAL(kw_value_str), kw_value_len, &status );
     738          36 :                         if (status == U_BUFFER_OVERFLOW_ERROR) {
     739           0 :                                 status = U_ZERO_ERROR;
     740           0 :                                 kw_value_str = zend_string_extend(kw_value_str, kw_value_len, 0);
     741           0 :                                 kw_value_len=uloc_getKeywordValue( loc_name,kw_key, ZSTR_VAL(kw_value_str), kw_value_len+1, &status );
     742          36 :                         } else if(!U_FAILURE(status)) {
     743          72 :                                 kw_value_str = zend_string_truncate(kw_value_str, kw_value_len, 0);
     744             :                         }
     745          36 :                         if (U_FAILURE(status)) {
     746           0 :                                 intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "locale_get_keywords: Error encountered while getting the keyword  value for the  keyword", 0 );
     747           0 :                                 if( kw_value_str){
     748             :                                         zend_string_free( kw_value_str );
     749             :                                 }
     750             :                                 zval_dtor(return_value);
     751           0 :                         RETURN_FALSE;
     752             :                         }
     753             : 
     754          36 :                 add_assoc_str( return_value, (char *)kw_key, kw_value_str);
     755             :                 } /* end of while */
     756             : 
     757             :         } /* end of if e!=NULL */
     758             : 
     759          74 :     uenum_close( e );
     760             : }
     761             : /* }}} */
     762             : 
     763             :  /* {{{ proto static string Locale::canonicalize($locale)
     764             :  * @return string the canonicalized locale
     765             :  * }}} */
     766             :  /* {{{ proto static string locale_canonicalize(Locale $loc, string $locale)
     767             :  * @param string $locale        The locale string to canonicalize
     768             :  */
     769         395 : PHP_FUNCTION(locale_canonicalize)
     770             : {
     771         395 :         get_icu_value_src_php( LOC_CANONICALIZE_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
     772         395 : }
     773             : /* }}} */
     774             : 
     775             : /* {{{ append_key_value
     776             : * Internal function which is called from locale_compose
     777             : * gets the value for the key_name and appends to the loc_name
     778             : * returns 1 if successful , -1 if not found ,
     779             : * 0 if array element is not a string , -2 if buffer-overflow
     780             : */
     781          84 : static int append_key_value(smart_str* loc_name, HashTable* hash_arr, char* key_name)
     782             : {
     783             :         zval *ele_value;
     784             : 
     785          84 :         if ((ele_value = zend_hash_str_find(hash_arr , key_name, strlen(key_name))) != NULL ) {
     786          52 :                 if(Z_TYPE_P(ele_value)!= IS_STRING ){
     787             :                         /* element value is not a string */
     788           2 :                         return FAILURE;
     789             :                 }
     790          80 :                 if(strcmp(key_name, LOC_LANG_TAG) != 0 &&
     791          30 :                    strcmp(key_name, LOC_GRANDFATHERED_LANG_TAG)!=0 ) {
     792             :                         /* not lang or grandfathered tag */
     793             :                         smart_str_appendl(loc_name, SEPARATOR , sizeof(SEPARATOR)-1);
     794             :                 }
     795          50 :                 smart_str_appendl(loc_name, Z_STRVAL_P(ele_value) , Z_STRLEN_P(ele_value));
     796          50 :                 return SUCCESS;
     797             :         }
     798             : 
     799          32 :         return LOC_NOT_FOUND;
     800             : }
     801             : /* }}} */
     802             : 
     803             : /* {{{ append_prefix , appends the prefix needed
     804             : * e.g. private adds 'x'
     805             : */
     806          18 : static void add_prefix(smart_str* loc_name, char* key_name)
     807             : {
     808          18 :         if( strncmp(key_name , LOC_PRIVATE_TAG , 7) == 0 ){
     809             :                 smart_str_appendl(loc_name, SEPARATOR , sizeof(SEPARATOR)-1);
     810             :                 smart_str_appendl(loc_name, PRIVATE_PREFIX , sizeof(PRIVATE_PREFIX)-1);
     811             :         }
     812          18 : }
     813             : /* }}} */
     814             : 
     815             : /* {{{ append_multiple_key_values
     816             : * Internal function which is called from locale_compose
     817             : * gets the multiple values for the key_name and appends to the loc_name
     818             : * used for 'variant','extlang','private'
     819             : * returns 1 if successful , -1 if not found ,
     820             : * 0 if array element is not a string , -2 if buffer-overflow
     821             : */
     822          56 : static int append_multiple_key_values(smart_str* loc_name, HashTable* hash_arr, char* key_name)
     823             : {
     824             :         zval    *ele_value;
     825          56 :         int     i               = 0;
     826          56 :         int     isFirstSubtag   = 0;
     827          56 :         int     max_value       = 0;
     828             : 
     829             :         /* Variant/ Extlang/Private etc. */
     830          56 :         if ((ele_value = zend_hash_str_find( hash_arr , key_name , strlen(key_name))) != NULL) {
     831           6 :                 if( Z_TYPE_P(ele_value) == IS_STRING ){
     832           0 :                         add_prefix( loc_name , key_name);
     833             : 
     834             :                         smart_str_appendl(loc_name, SEPARATOR , sizeof(SEPARATOR)-1);
     835           0 :                         smart_str_appendl(loc_name, Z_STRVAL_P(ele_value) , Z_STRLEN_P(ele_value));
     836           0 :                         return SUCCESS;
     837           6 :                 } else if(Z_TYPE_P(ele_value) == IS_ARRAY ) {
     838           6 :                         HashTable *arr = Z_ARRVAL_P(ele_value);
     839             :                         zval *data;
     840             : 
     841          30 :                         ZEND_HASH_FOREACH_VAL(arr, data) {
     842          12 :                                 if(Z_TYPE_P(data) != IS_STRING) {
     843           0 :                                         return FAILURE;
     844             :                                 }
     845          12 :                                 if (isFirstSubtag++ == 0){
     846           6 :                                         add_prefix(loc_name , key_name);
     847             :                                 }
     848             :                                 smart_str_appendl(loc_name, SEPARATOR , sizeof(SEPARATOR)-1);
     849          12 :                                 smart_str_appendl(loc_name, Z_STRVAL_P(data) , Z_STRLEN_P(data));
     850             :                         } ZEND_HASH_FOREACH_END();
     851           6 :                         return SUCCESS;
     852             :                 } else {
     853           0 :                         return FAILURE;
     854             :                 }
     855             :         } else {
     856             :                 char cur_key_name[31];
     857             :                 /* Decide the max_value: the max. no. of elements allowed */
     858          50 :                 if( strcmp(key_name , LOC_VARIANT_TAG) ==0 ){
     859          16 :                         max_value  = MAX_NO_VARIANT;
     860             :                 }
     861          50 :                 if( strcmp(key_name , LOC_EXTLANG_TAG) ==0 ){
     862          18 :                         max_value  = MAX_NO_EXTLANG;
     863             :                 }
     864          50 :                 if( strcmp(key_name , LOC_PRIVATE_TAG) ==0 ){
     865          16 :                         max_value  = MAX_NO_PRIVATE;
     866             :                 }
     867             : 
     868             :                 /* Multiple variant values as variant0, variant1 ,variant2 */
     869          50 :                 isFirstSubtag = 0;
     870         582 :                 for( i=0 ; i< max_value; i++ ){
     871         534 :                         snprintf( cur_key_name , 30, "%s%d", key_name , i);
     872         534 :                         if ((ele_value = zend_hash_str_find( hash_arr , cur_key_name , strlen(cur_key_name))) != NULL) {
     873          26 :                                 if( Z_TYPE_P(ele_value)!= IS_STRING ){
     874             :                                         /* variant is not a string */
     875           2 :                                         return FAILURE;
     876             :                                 }
     877             :                                 /* Add the contents */
     878          24 :                                 if (isFirstSubtag++ == 0){
     879          12 :                                         add_prefix(loc_name , cur_key_name);
     880             :                                 }
     881             :                                 smart_str_appendl(loc_name, SEPARATOR , sizeof(SEPARATOR)-1);
     882          24 :                                 smart_str_appendl(loc_name, Z_STRVAL_P(ele_value) , Z_STRLEN_P(ele_value));
     883             :                         }
     884             :                 } /* end of for */
     885             :         } /* end of else */
     886             : 
     887          48 :         return SUCCESS;
     888             : }
     889             : /* }}} */
     890             : 
     891             : /*{{{
     892             : * If applicable sets error message and aborts locale_compose gracefully
     893             : * returns 0  if locale_compose needs to be aborted
     894             : * otherwise returns 1
     895             : */
     896         138 : static int handleAppendResult( int result, smart_str* loc_name)
     897             : {
     898         138 :         intl_error_reset( NULL );
     899         138 :         if( result == FAILURE) {
     900           4 :                 intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
     901             :                          "locale_compose: parameter array element is not a string", 0 );
     902             :                 smart_str_free(loc_name);
     903           4 :                 return 0;
     904             :         }
     905         134 :         return 1;
     906             : }
     907             : /* }}} */
     908             : 
     909             : #define RETURN_SMART_STR(str) smart_str_0((str)); RETURN_NEW_STR((str)->s)
     910             : /* {{{ proto static string Locale::composeLocale($array)
     911             : * Creates a locale by combining the parts of locale-ID passed
     912             : * }}} */
     913             : /* {{{ proto static string compose_locale($array)
     914             : * Creates a locale by combining the parts of locale-ID passed
     915             : * }}} */
     916          25 : PHP_FUNCTION(locale_compose)
     917             : {
     918          25 :         smart_str       loc_name_s = {0};
     919          25 :         smart_str *loc_name = &loc_name_s;
     920          25 :         zval*                   arr     = NULL;
     921          25 :         HashTable*              hash_arr = NULL;
     922          25 :         int                     result = 0;
     923             : 
     924          25 :         intl_error_reset( NULL );
     925             : 
     926          25 :         if(zend_parse_parameters( ZEND_NUM_ARGS(), "a",
     927             :                 &arr) == FAILURE)
     928             :         {
     929           1 :                 intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
     930             :                          "locale_compose: unable to parse input params", 0 );
     931           1 :                 RETURN_FALSE;
     932             :         }
     933             : 
     934          24 :         hash_arr = Z_ARRVAL_P( arr );
     935             : 
     936          24 :         if( !hash_arr || zend_hash_num_elements( hash_arr ) == 0 )
     937           0 :                 RETURN_FALSE;
     938             : 
     939             :         /* Check for grandfathered first */
     940          24 :         result = append_key_value(loc_name, hash_arr,  LOC_GRANDFATHERED_LANG_TAG);
     941          24 :         if( result == SUCCESS){
     942           0 :                 RETURN_SMART_STR(loc_name);
     943             :         }
     944          24 :         if( !handleAppendResult( result, loc_name)){
     945           0 :                 RETURN_FALSE;
     946             :         }
     947             : 
     948             :         /* Not grandfathered */
     949          24 :         result = append_key_value(loc_name, hash_arr , LOC_LANG_TAG);
     950          24 :         if( result == LOC_NOT_FOUND ){
     951           2 :                 intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
     952             :                 "locale_compose: parameter array does not contain 'language' tag.", 0 );
     953             :                 smart_str_free(loc_name);
     954           2 :                 RETURN_FALSE;
     955             :         }
     956          22 :         if( !handleAppendResult( result, loc_name)){
     957           2 :                 RETURN_FALSE;
     958             :         }
     959             : 
     960             :         /* Extlang */
     961          20 :         result = append_multiple_key_values(loc_name, hash_arr , LOC_EXTLANG_TAG);
     962          20 :         if( !handleAppendResult( result, loc_name)){
     963           2 :                 RETURN_FALSE;
     964             :         }
     965             : 
     966             :         /* Script */
     967          18 :         result = append_key_value(loc_name, hash_arr , LOC_SCRIPT_TAG);
     968          18 :         if( !handleAppendResult( result, loc_name)){
     969           0 :                 RETURN_FALSE;
     970             :         }
     971             : 
     972             :         /* Region */
     973          18 :         result = append_key_value( loc_name, hash_arr , LOC_REGION_TAG);
     974          18 :         if( !handleAppendResult( result, loc_name)){
     975           0 :                 RETURN_FALSE;
     976             :         }
     977             : 
     978             :         /* Variant */
     979          18 :         result = append_multiple_key_values( loc_name, hash_arr , LOC_VARIANT_TAG);
     980          18 :         if( !handleAppendResult( result, loc_name)){
     981           0 :                 RETURN_FALSE;
     982             :         }
     983             : 
     984             :         /* Private */
     985          18 :         result = append_multiple_key_values( loc_name, hash_arr , LOC_PRIVATE_TAG);
     986          18 :         if( !handleAppendResult( result, loc_name)){
     987           0 :                 RETURN_FALSE;
     988             :         }
     989             : 
     990          18 :         RETURN_SMART_STR(loc_name);
     991             : }
     992             : /* }}} */
     993             : 
     994             : 
     995             : /*{{{
     996             : * Parses the locale and returns private subtags  if existing
     997             : * else returns NULL
     998             : * e.g. for locale='en_US-x-prv1-prv2-prv3'
     999             : * returns a pointer to the string 'prv1-prv2-prv3'
    1000             : */
    1001          68 : static zend_string* get_private_subtags(const char* loc_name)
    1002             : {
    1003          68 :         zend_string* result = NULL;
    1004          68 :         zend_off_t   singletonPos = 0;
    1005          68 :         size_t       len = 0;
    1006          68 :         const char*  mod_loc_name =NULL;
    1007             : 
    1008          68 :         if( loc_name && (len = strlen(loc_name)>0 ) ){
    1009          68 :                 mod_loc_name = loc_name ;
    1010          68 :                 len   = strlen(mod_loc_name);
    1011         152 :                 while( (singletonPos = getSingletonPos(mod_loc_name)) > -1){
    1012          26 :                         if( (*(mod_loc_name+singletonPos)=='x') || (*(mod_loc_name+singletonPos)=='X') ){
    1013             :                                 /* private subtag start found */
    1014          10 :                                 if( singletonPos + 2 ==  len){
    1015             :                                         /* loc_name ends with '-x-' ; return  NULL */
    1016             :                                 }
    1017             :                                 else{
    1018             :                                         /* result = mod_loc_name + singletonPos +2; */
    1019          20 :                                         result = zend_string_init(mod_loc_name + singletonPos+2  , (len -( singletonPos +2) ), 0);
    1020             :                                 }
    1021          10 :                                 break;
    1022             :                         }
    1023             :                         else{
    1024          16 :                                 if((size_t)(singletonPos + 1) >= len){
    1025             :                                         /* String end */
    1026           0 :                                         break;
    1027             :                                 } else {
    1028             :                                         /* singleton found but not a private subtag , hence check further in the string for the private subtag */
    1029          16 :                                         mod_loc_name = mod_loc_name + singletonPos +1;
    1030          16 :                                         len = strlen(mod_loc_name);
    1031             :                                 }
    1032             :                         }
    1033             :                 } /* end of while */
    1034             :         }
    1035             : 
    1036          68 :         return result;
    1037             : }
    1038             : /* }}} */
    1039             : 
    1040             : /* {{{ code used by locale_parse
    1041             : */
    1042         340 : static int add_array_entry(const char* loc_name, zval* hash_arr, char* key_name)
    1043             : {
    1044         340 :         zend_string*   key_value        = NULL;
    1045         340 :         char*   cur_key_name    = NULL;
    1046         340 :         char*   token           = NULL;
    1047         340 :         char*   last_ptr        = NULL;
    1048             : 
    1049         340 :         int     result          = 0;
    1050         340 :         int     cur_result      = 0;
    1051         340 :         int     cnt             = 0;
    1052             : 
    1053             : 
    1054         340 :         if( strcmp(key_name , LOC_PRIVATE_TAG)==0 ){
    1055          68 :                 key_value = get_private_subtags( loc_name );
    1056          68 :                 result = 1;
    1057             :         } else {
    1058         272 :                 key_value = get_icu_value_internal( loc_name , key_name , &result,1 );
    1059             :         }
    1060         612 :         if( (strcmp(key_name , LOC_PRIVATE_TAG)==0) ||
    1061         272 :                 ( strcmp(key_name , LOC_VARIANT_TAG)==0) ){
    1062         136 :                 if( result > 0 && key_value){
    1063             :                         /* Tokenize on the "_" or "-"  */
    1064          26 :                         token = php_strtok_r( key_value->val , DELIMITER ,&last_ptr);
    1065          26 :                         if( cur_key_name ){
    1066           0 :                                 efree( cur_key_name);
    1067             :                         }
    1068          26 :                         cur_key_name = (char*)ecalloc( 25,  25);
    1069          26 :                         sprintf( cur_key_name , "%s%d", key_name , cnt++);
    1070          26 :                         add_assoc_string( hash_arr, cur_key_name , token);
    1071             :                         /* tokenize on the "_" or "-" and stop  at singleton if any */
    1072          58 :                         while( (token = php_strtok_r(NULL , DELIMITER , &last_ptr)) && (strlen(token)>1) ){
    1073           6 :                                 sprintf( cur_key_name , "%s%d", key_name , cnt++);
    1074           6 :                                 add_assoc_string( hash_arr, cur_key_name , token);
    1075             :                         }
    1076             : /*
    1077             :                         if( strcmp(key_name, LOC_PRIVATE_TAG) == 0 ){
    1078             :                         }
    1079             : */
    1080             :                 }
    1081         272 :                 if (key_value) {
    1082             :                         zend_string_release(key_value);
    1083             :                 }
    1084             :         } else {
    1085         204 :                 if( result == 1 ){
    1086         120 :                         add_assoc_str( hash_arr, key_name , key_value);
    1087         120 :                         cur_result = 1;
    1088          84 :                 } else if (key_value) {
    1089             :                         zend_string_release(key_value);
    1090             :                 }
    1091             :         }
    1092             : 
    1093         340 :         if( cur_key_name ){
    1094          26 :                 efree( cur_key_name);
    1095             :         }
    1096             :         /*if( key_name != LOC_PRIVATE_TAG && key_value){*/
    1097         340 :         return cur_result;
    1098             : }
    1099             : /* }}} */
    1100             : 
    1101             : /* {{{ proto static array Locale::parseLocale($locale)
    1102             : * parses a locale-id into an array the different parts of it
    1103             :  }}} */
    1104             : /* {{{ proto static array parse_locale($locale)
    1105             : * parses a locale-id into an array the different parts of it
    1106             : */
    1107          73 : PHP_FUNCTION(locale_parse)
    1108             : {
    1109          73 :     const char* loc_name        = NULL;
    1110          73 :     size_t         loc_name_len    = 0;
    1111          73 :     int         grOffset        = 0;
    1112             : 
    1113          73 :     intl_error_reset( NULL );
    1114             : 
    1115          73 :     if(zend_parse_parameters( ZEND_NUM_ARGS(), "s",
    1116             :         &loc_name, &loc_name_len ) == FAILURE)
    1117             :     {
    1118           1 :         intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
    1119             :              "locale_parse: unable to parse input params", 0 );
    1120             : 
    1121           2 :         RETURN_FALSE;
    1122             :     }
    1123             : 
    1124          72 :     INTL_CHECK_LOCALE_LEN(strlen(loc_name));
    1125             : 
    1126          72 :     if(loc_name_len == 0) {
    1127           0 :         loc_name = intl_locale_get_default();
    1128             :     }
    1129             : 
    1130          72 :         array_init( return_value );
    1131             : 
    1132          72 :         grOffset =  findOffset( LOC_GRANDFATHERED , loc_name );
    1133          72 :         if( grOffset >= 0 ){
    1134           4 :                 add_assoc_string( return_value , LOC_GRANDFATHERED_LANG_TAG, (char *)loc_name);
    1135             :         }
    1136             :         else{
    1137             :                 /* Not grandfathered */
    1138          68 :                 add_array_entry( loc_name , return_value , LOC_LANG_TAG);
    1139          68 :                 add_array_entry( loc_name , return_value , LOC_SCRIPT_TAG);
    1140          68 :                 add_array_entry( loc_name , return_value , LOC_REGION_TAG);
    1141          68 :                 add_array_entry( loc_name , return_value , LOC_VARIANT_TAG);
    1142          68 :                 add_array_entry( loc_name , return_value , LOC_PRIVATE_TAG);
    1143             :         }
    1144             : }
    1145             : /* }}} */
    1146             : 
    1147             : /* {{{ proto static array Locale::getAllVariants($locale)
    1148             : * gets an array containing the list of variants, or null
    1149             :  }}} */
    1150             : /* {{{ proto static array locale_get_all_variants($locale)
    1151             : * gets an array containing the list of variants, or null
    1152             : */
    1153          23 : PHP_FUNCTION(locale_get_all_variants)
    1154             : {
    1155          23 :         const char*     loc_name        = NULL;
    1156          23 :         size_t                  loc_name_len    = 0;
    1157             : 
    1158          23 :         int     result          = 0;
    1159          23 :         char*   token           = NULL;
    1160          23 :         zend_string*    variant         = NULL;
    1161          23 :         char*   saved_ptr       = NULL;
    1162             : 
    1163          23 :         intl_error_reset( NULL );
    1164             : 
    1165          23 :         if(zend_parse_parameters( ZEND_NUM_ARGS(), "s",
    1166             :         &loc_name, &loc_name_len ) == FAILURE)
    1167             :         {
    1168           1 :                 intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
    1169             :              "locale_parse: unable to parse input params", 0 );
    1170             : 
    1171           2 :                 RETURN_FALSE;
    1172             :         }
    1173             : 
    1174          22 :         if(loc_name_len == 0) {
    1175           0 :                 loc_name = intl_locale_get_default();
    1176           0 :                 loc_name_len = strlen(loc_name);
    1177             :         }
    1178             : 
    1179          22 :         INTL_CHECK_LOCALE_LEN(loc_name_len);
    1180             : 
    1181          22 :         array_init( return_value );
    1182             : 
    1183             :         /* If the locale is grandfathered, stop, no variants */
    1184          22 :         if( findOffset( LOC_GRANDFATHERED , loc_name ) >=  0 ){
    1185             :                 /* ("Grandfathered Tag. No variants."); */
    1186             :         }
    1187             :         else {
    1188             :         /* Call ICU variant */
    1189          10 :                 variant = get_icu_value_internal( loc_name , LOC_VARIANT_TAG , &result ,0);
    1190          10 :                 if( result > 0 && variant){
    1191             :                         /* Tokenize on the "_" or "-" */
    1192          10 :                         token = php_strtok_r( variant->val , DELIMITER , &saved_ptr);
    1193          10 :                         add_next_index_stringl( return_value, token , strlen(token));
    1194             :                         /* tokenize on the "_" or "-" and stop  at singleton if any */
    1195          26 :                         while( (token = php_strtok_r(NULL , DELIMITER, &saved_ptr)) && (strlen(token)>1) ){
    1196           6 :                                 add_next_index_stringl( return_value, token , strlen(token));
    1197             :                         }
    1198             :                 }
    1199          10 :                 if( variant ){
    1200             :                         zend_string_release( variant );
    1201             :                 }
    1202             :         }
    1203             : 
    1204             : 
    1205             : }
    1206             : /* }}} */
    1207             : 
    1208             : /*{{{
    1209             : * Converts to lower case and also replaces all hyphens with the underscore
    1210             : */
    1211        1345 : static int strToMatch(const char* str ,char *retstr)
    1212             : {
    1213        1345 :         char*   anchor  = NULL;
    1214        1345 :         const char*     anchor1 = NULL;
    1215        1345 :         int     result  = 0;
    1216             : 
    1217        1345 :     if( (!str) || str[0] == '\0'){
    1218           0 :         return result;
    1219             :     } else {
    1220        1345 :         anchor = retstr;
    1221        1345 :         anchor1 = str;
    1222       14771 :         while( (*str)!='\0' ){
    1223       12081 :                 if( *str == '-' ){
    1224         720 :                         *retstr =  '_';
    1225             :                 } else {
    1226       11361 :                         *retstr = tolower(*str);
    1227             :                 }
    1228       12081 :             str++;
    1229       12081 :             retstr++;
    1230             :         }
    1231        1345 :         *retstr = '\0';
    1232        1345 :         retstr=  anchor;
    1233        1345 :         str=  anchor1;
    1234        1345 :         result = 1;
    1235             :     }
    1236             : 
    1237        1345 :     return(result);
    1238             : }
    1239             : /* }}} */
    1240             : 
    1241             : /* {{{ proto static boolean Locale::filterMatches(string $langtag, string $locale[, bool $canonicalize])
    1242             : * Checks if a $langtag filter matches with $locale according to RFC 4647's basic filtering algorithm
    1243             : */
    1244             : /* }}} */
    1245             : /* {{{ proto boolean locale_filter_matches(string $langtag, string $locale[, bool $canonicalize])
    1246             : * Checks if a $langtag filter matches with $locale according to RFC 4647's basic filtering algorithm
    1247             : */
    1248         386 : PHP_FUNCTION(locale_filter_matches)
    1249             : {
    1250         386 :         char*           lang_tag        = NULL;
    1251         386 :         size_t          lang_tag_len    = 0;
    1252         386 :         const char*     loc_range       = NULL;
    1253         386 :         size_t          loc_range_len   = 0;
    1254             : 
    1255         386 :         int             result          = 0;
    1256         386 :         char*           token           = 0;
    1257         386 :         char*           chrcheck        = NULL;
    1258             : 
    1259         386 :         zend_string*    can_lang_tag    = NULL;
    1260         386 :         zend_string*    can_loc_range   = NULL;
    1261             : 
    1262         386 :         char*           cur_lang_tag    = NULL;
    1263         386 :         char*           cur_loc_range   = NULL;
    1264             : 
    1265         386 :         zend_bool       boolCanonical   = 0;
    1266         386 :         UErrorCode      status          = U_ZERO_ERROR;
    1267             : 
    1268         386 :         intl_error_reset( NULL );
    1269             : 
    1270         386 :         if(zend_parse_parameters( ZEND_NUM_ARGS(), "ss|b",
    1271             :                 &lang_tag, &lang_tag_len , &loc_range , &loc_range_len ,
    1272             :                 &boolCanonical) == FAILURE)
    1273             :         {
    1274           1 :                 intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
    1275             :                 "locale_filter_matches: unable to parse input params", 0 );
    1276             : 
    1277           1 :                 RETURN_FALSE;
    1278             :         }
    1279             : 
    1280         385 :         if(loc_range_len == 0) {
    1281           0 :                 loc_range = intl_locale_get_default();
    1282           0 :                 loc_range_len = strlen(loc_range);
    1283             :         }
    1284             : 
    1285         385 :         if( strcmp(loc_range,"*")==0){
    1286           0 :                 RETURN_TRUE;
    1287             :         }
    1288             : 
    1289         385 :         INTL_CHECK_LOCALE_LEN(loc_range_len);
    1290         385 :         INTL_CHECK_LOCALE_LEN(lang_tag_len);
    1291             : 
    1292         385 :         if( boolCanonical ){
    1293             :                 /* canonicalize loc_range */
    1294         193 :                 can_loc_range=get_icu_value_internal( loc_range , LOC_CANONICALIZE_TAG , &result , 0);
    1295         193 :                 if( result <=0) {
    1296           0 :                         intl_error_set( NULL, status,
    1297             :                                 "locale_filter_matches : unable to canonicalize loc_range" , 0 );
    1298           0 :                         RETURN_FALSE;
    1299             :                 }
    1300             : 
    1301             :                 /* canonicalize lang_tag */
    1302         193 :                 can_lang_tag = get_icu_value_internal( lang_tag , LOC_CANONICALIZE_TAG , &result ,  0);
    1303         193 :                 if( result <=0) {
    1304           0 :                         intl_error_set( NULL, status,
    1305             :                                 "locale_filter_matches : unable to canonicalize lang_tag" , 0 );
    1306           0 :                         RETURN_FALSE;
    1307             :                 }
    1308             : 
    1309             :                 /* Convert to lower case for case-insensitive comparison */
    1310         193 :                 cur_lang_tag = ecalloc( 1, can_lang_tag->len + 1);
    1311             : 
    1312             :                 /* Convert to lower case for case-insensitive comparison */
    1313         193 :                 result = strToMatch( can_lang_tag->val , cur_lang_tag);
    1314         193 :                 if( result == 0) {
    1315           0 :                         efree( cur_lang_tag );
    1316             :                         zend_string_release( can_lang_tag );
    1317           0 :                         RETURN_FALSE;
    1318             :                 }
    1319             : 
    1320         193 :                 cur_loc_range = ecalloc( 1, can_loc_range->len + 1);
    1321         193 :                 result = strToMatch( can_loc_range->val , cur_loc_range );
    1322         193 :                 if( result == 0) {
    1323           0 :                         efree( cur_lang_tag );
    1324             :                         zend_string_release( can_lang_tag );
    1325           0 :                         efree( cur_loc_range );
    1326             :                         zend_string_release( can_loc_range );
    1327           0 :                         RETURN_FALSE;
    1328             :                 }
    1329             : 
    1330             :                 /* check if prefix */
    1331         193 :                 token   = strstr( cur_lang_tag , cur_loc_range );
    1332             : 
    1333         193 :                 if( token && (token==cur_lang_tag) ){
    1334             :                         /* check if the char. after match is SEPARATOR */
    1335          32 :                         chrcheck = token + (strlen(cur_loc_range));
    1336          32 :                         if( isIDSeparator(*chrcheck) || isEndOfTag(*chrcheck) ){
    1337          30 :                                 if( cur_lang_tag){
    1338          30 :                                         efree( cur_lang_tag );
    1339             :                                 }
    1340          30 :                                 if( cur_loc_range){
    1341          30 :                                         efree( cur_loc_range );
    1342             :                                 }
    1343          30 :                                 if( can_lang_tag){
    1344             :                                         zend_string_release( can_lang_tag );
    1345             :                                 }
    1346          30 :                                 if( can_loc_range){
    1347             :                                         zend_string_release( can_loc_range );
    1348             :                                 }
    1349          30 :                                 RETURN_TRUE;
    1350             :                         }
    1351             :                 }
    1352             : 
    1353             :                 /* No prefix as loc_range */
    1354         163 :                 if( cur_lang_tag){
    1355         163 :                         efree( cur_lang_tag );
    1356             :                 }
    1357         163 :                 if( cur_loc_range){
    1358         163 :                         efree( cur_loc_range );
    1359             :                 }
    1360         163 :                 if( can_lang_tag){
    1361             :                         zend_string_release( can_lang_tag );
    1362             :                 }
    1363         163 :                 if( can_loc_range){
    1364             :                         zend_string_release( can_loc_range );
    1365             :                 }
    1366         163 :                 RETURN_FALSE;
    1367             : 
    1368             :         } /* end of if isCanonical */
    1369             :         else{
    1370             :                 /* Convert to lower case for case-insensitive comparison */
    1371         192 :                 cur_lang_tag = ecalloc( 1, strlen(lang_tag ) + 1);
    1372             : 
    1373         192 :                 result = strToMatch( lang_tag , cur_lang_tag);
    1374         192 :                 if( result == 0) {
    1375           0 :                         efree( cur_lang_tag );
    1376           0 :                         RETURN_FALSE;
    1377             :                 }
    1378         192 :                 cur_loc_range = ecalloc( 1, strlen(loc_range ) + 1);
    1379         192 :                 result = strToMatch( loc_range , cur_loc_range );
    1380         192 :                 if( result == 0) {
    1381           0 :                         efree( cur_lang_tag );
    1382           0 :                         efree( cur_loc_range );
    1383           0 :                         RETURN_FALSE;
    1384             :                 }
    1385             : 
    1386             :                 /* check if prefix */
    1387         192 :                 token   = strstr( cur_lang_tag , cur_loc_range );
    1388             : 
    1389         192 :                 if( token && (token==cur_lang_tag) ){
    1390             :                         /* check if the char. after match is SEPARATOR */
    1391          32 :                         chrcheck = token + (strlen(cur_loc_range));
    1392          32 :                         if( isIDSeparator(*chrcheck) || isEndOfTag(*chrcheck) ){
    1393          30 :                                 if( cur_lang_tag){
    1394          30 :                                         efree( cur_lang_tag );
    1395             :                                 }
    1396          30 :                                 if( cur_loc_range){
    1397          30 :                                         efree( cur_loc_range );
    1398             :                                 }
    1399          30 :                                 RETURN_TRUE;
    1400             :                         }
    1401             :                 }
    1402             : 
    1403             :                 /* No prefix as loc_range */
    1404         162 :                 if( cur_lang_tag){
    1405         162 :                         efree( cur_lang_tag );
    1406             :                 }
    1407         162 :                 if( cur_loc_range){
    1408         162 :                         efree( cur_loc_range );
    1409             :                 }
    1410         162 :                 RETURN_FALSE;
    1411             : 
    1412             :         }
    1413             : }
    1414             : /* }}} */
    1415             : 
    1416          26 : static void array_cleanup( char* arr[] , int arr_size)
    1417             : {
    1418          26 :         int i=0;
    1419         387 :         for( i=0; i< arr_size; i++ ){
    1420         361 :                 if( arr[i*2] ){
    1421         361 :                         efree( arr[i*2]);
    1422             :                 }
    1423             :         }
    1424          26 :         efree(arr);
    1425          26 : }
    1426             : 
    1427             : #define LOOKUP_CLEAN_RETURN(value)      array_cleanup(cur_arr, cur_arr_len); return (value)
    1428             : /* {{{
    1429             : * returns the lookup result to lookup_loc_range_src_php
    1430             : * internal function
    1431             : */
    1432          26 : static zend_string* lookup_loc_range(const char* loc_range, HashTable* hash_arr, int canonicalize )
    1433             : {
    1434          26 :         int     i = 0;
    1435          26 :         int     cur_arr_len = 0;
    1436          26 :         int result = 0;
    1437             : 
    1438          26 :         zend_string* lang_tag = NULL;
    1439          26 :         zval* ele_value = NULL;
    1440          26 :         char** cur_arr = NULL;
    1441             : 
    1442          26 :         char* cur_loc_range     = NULL;
    1443          26 :         zend_string* can_loc_range      = NULL;
    1444          26 :         zend_off_t saved_pos = 0;
    1445             : 
    1446          26 :         zend_string* return_value = NULL;
    1447             : 
    1448          26 :         cur_arr = ecalloc(zend_hash_num_elements(hash_arr)*2, sizeof(char *));
    1449         748 :         ZEND_HASH_FOREACH_VAL(hash_arr, ele_value) {
    1450             :         /* convert the array to lowercase , also replace hyphens with the underscore and store it in cur_arr */
    1451         361 :                 if(Z_TYPE_P(ele_value)!= IS_STRING) {
    1452             :                         /* element value is not a string */
    1453           0 :                         intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: locale array element is not a string", 0);
    1454           0 :                         LOOKUP_CLEAN_RETURN(NULL);
    1455             :                 }
    1456         361 :                 cur_arr[cur_arr_len*2] = estrndup(Z_STRVAL_P(ele_value), Z_STRLEN_P(ele_value));
    1457         361 :                 result = strToMatch(Z_STRVAL_P(ele_value), cur_arr[cur_arr_len*2]);
    1458         361 :                 if(result == 0) {
    1459           0 :                         intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: unable to canonicalize lang_tag", 0);
    1460           0 :                         LOOKUP_CLEAN_RETURN(NULL);
    1461             :                 }
    1462         361 :                 cur_arr[cur_arr_len*2+1] = Z_STRVAL_P(ele_value);
    1463         361 :                 cur_arr_len++ ;
    1464             :         } ZEND_HASH_FOREACH_END(); /* end of for */
    1465             : 
    1466             :         /* Canonicalize array elements */
    1467          26 :         if(canonicalize) {
    1468         203 :                 for(i=0; i<cur_arr_len; i++) {
    1469         188 :                         lang_tag = get_icu_value_internal(cur_arr[i*2], LOC_CANONICALIZE_TAG, &result, 0);
    1470         188 :                         if(result != 1 || lang_tag == NULL || !lang_tag->val[0]) {
    1471           0 :                                 if(lang_tag) {
    1472             :                                         zend_string_release(lang_tag);
    1473             :                                 }
    1474           0 :                                 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: unable to canonicalize lang_tag" , 0);
    1475           0 :                                 LOOKUP_CLEAN_RETURN(NULL);
    1476             :                         }
    1477         188 :                         cur_arr[i*2] = erealloc(cur_arr[i*2], lang_tag->len+1);
    1478         188 :                         result = strToMatch(lang_tag->val, cur_arr[i*2]);
    1479             :                         zend_string_release(lang_tag);
    1480         188 :                         if(result == 0) {
    1481           0 :                                 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: unable to canonicalize lang_tag" , 0);
    1482           0 :                                 LOOKUP_CLEAN_RETURN(NULL);
    1483             :                         }
    1484             :                 }
    1485             : 
    1486             :         }
    1487             : 
    1488          26 :         if(canonicalize) {
    1489             :                 /* Canonicalize the loc_range */
    1490          15 :                 can_loc_range = get_icu_value_internal(loc_range, LOC_CANONICALIZE_TAG, &result , 0);
    1491          15 :                 if( result != 1 || can_loc_range == NULL || !can_loc_range->val[0]) {
    1492             :                         /* Error */
    1493           0 :                         intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: unable to canonicalize loc_range" , 0 );
    1494           0 :                         if(can_loc_range) {
    1495             :                                 zend_string_release(can_loc_range);
    1496             :                         }
    1497           0 :                         LOOKUP_CLEAN_RETURN(NULL);
    1498             :                 } else {
    1499          15 :                         loc_range = can_loc_range->val;
    1500             :                 }
    1501             :         }
    1502             : 
    1503          26 :         cur_loc_range = ecalloc(1, strlen(loc_range)+1);
    1504             :         /* convert to lower and replace hyphens */
    1505          26 :         result = strToMatch(loc_range, cur_loc_range);
    1506          26 :         if(can_loc_range) {
    1507             :                 zend_string_release(can_loc_range);
    1508             :         }
    1509          26 :         if(result == 0) {
    1510           0 :                 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: unable to canonicalize lang_tag" , 0);
    1511           0 :                 LOOKUP_CLEAN_RETURN(NULL);
    1512             :         }
    1513             : 
    1514             :         /* Lookup for the lang_tag match */
    1515          26 :         saved_pos = strlen(cur_loc_range);
    1516          61 :         while(saved_pos > 0) {
    1517         270 :                 for(i=0; i< cur_arr_len; i++){
    1518         261 :                         if(cur_arr[i*2] != NULL && strlen(cur_arr[i*2]) == saved_pos && strncmp(cur_loc_range, cur_arr[i*2], saved_pos) == 0) {
    1519             :                                 /* Match found */
    1520          24 :                                 char *str = canonicalize ? cur_arr[i*2] : cur_arr[i*2+1];
    1521          48 :                                 return_value = zend_string_init(str, strlen(str), 0);
    1522          24 :                                 efree(cur_loc_range);
    1523          24 :                                 LOOKUP_CLEAN_RETURN(return_value);
    1524             :                         }
    1525             :                 }
    1526           9 :                 saved_pos = getStrrtokenPos(cur_loc_range, saved_pos);
    1527             :         }
    1528             : 
    1529             :         /* Match not found */
    1530           2 :         efree(cur_loc_range);
    1531           2 :         LOOKUP_CLEAN_RETURN(NULL);
    1532             : }
    1533             : /* }}} */
    1534             : 
    1535             : /* {{{ proto string Locale::lookup(array $langtag, string $locale[, bool $canonicalize[, string $default = null]])
    1536             : * Searchs the items in $langtag for the best match to the language
    1537             : * range
    1538             : */
    1539             : /* }}} */
    1540             : /* {{{ proto string locale_lookup(array $langtag, string $locale[, bool $canonicalize[, string $default = null]])
    1541             : * Searchs the items in $langtag for the best match to the language
    1542             : * range
    1543             : */
    1544          27 : PHP_FUNCTION(locale_lookup)
    1545             : {
    1546          27 :         zend_string*    fallback_loc_str        = NULL;
    1547          27 :         const char*     loc_range               = NULL;
    1548          27 :         size_t          loc_range_len           = 0;
    1549             : 
    1550          27 :         zval*           arr                             = NULL;
    1551          27 :         HashTable*      hash_arr                = NULL;
    1552          27 :         zend_bool       boolCanonical   = 0;
    1553          27 :         zend_string*    result_str      = NULL;
    1554             : 
    1555          27 :         intl_error_reset( NULL );
    1556             : 
    1557             : #if U_ICU_VERSION_MAJOR_NUM > 63
    1558             : # define BANG "!"
    1559             : #else
    1560             : # define BANG
    1561             : #endif  
    1562          27 :         if(zend_parse_parameters( ZEND_NUM_ARGS(), "as|bS" BANG, &arr, &loc_range, &loc_range_len,
    1563             :                 &boolCanonical,     &fallback_loc_str) == FAILURE) {
    1564           1 :                 intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "locale_lookup: unable to parse input params", 0 );
    1565           1 :                 RETURN_FALSE;
    1566             :         }
    1567             : #undef BANG
    1568             : 
    1569          26 :         if(loc_range_len == 0) {
    1570           4 :                 if(fallback_loc_str) {
    1571           3 :                         loc_range = ZSTR_VAL(fallback_loc_str);
    1572           3 :                         loc_range_len = ZSTR_LEN(fallback_loc_str);
    1573             :                 } else {
    1574           1 :                         loc_range = intl_locale_get_default();
    1575           1 :                         loc_range_len = strlen(loc_range);
    1576             :                 }
    1577             :         }
    1578             : 
    1579          26 :         hash_arr = Z_ARRVAL_P(arr);
    1580             : 
    1581          26 :         INTL_CHECK_LOCALE_LEN(loc_range_len);
    1582             : 
    1583          26 :         if( !hash_arr || zend_hash_num_elements( hash_arr ) == 0 ) {
    1584           0 :                 RETURN_EMPTY_STRING();
    1585             :         }
    1586             : 
    1587          26 :         result_str = lookup_loc_range(loc_range, hash_arr, boolCanonical);
    1588          26 :         if(result_str == NULL || ZSTR_VAL(result_str)[0] == '\0') {
    1589           2 :                 if( fallback_loc_str ) {
    1590           4 :                         result_str = zend_string_copy(fallback_loc_str);
    1591             :                 } else {
    1592           0 :                         RETURN_EMPTY_STRING();
    1593             :                 }
    1594             :         }
    1595             : 
    1596          26 :         RETURN_STR(result_str);
    1597             : }
    1598             : /* }}} */
    1599             : 
    1600             : /* {{{ proto string Locale::acceptFromHttp(string $http_accept)
    1601             : * Tries to find out best available locale based on HTTP �Accept-Language� header
    1602             : */
    1603             : /* }}} */
    1604             : /* {{{ proto string locale_accept_from_http(string $http_accept)
    1605             : * Tries to find out best available locale based on HTTP �Accept-Language� header
    1606             : */
    1607          17 : PHP_FUNCTION(locale_accept_from_http)
    1608             : {
    1609             :         UEnumeration *available;
    1610          17 :         char *http_accept = NULL;
    1611             :         size_t http_accept_len;
    1612          17 :         UErrorCode status = 0;
    1613             :         int len;
    1614             :         char resultLocale[INTL_MAX_LOCALE_LEN+1];
    1615             :         UAcceptResult outResult;
    1616             : 
    1617          17 :         if(zend_parse_parameters( ZEND_NUM_ARGS(), "s", &http_accept, &http_accept_len) == FAILURE)
    1618             :         {
    1619           3 :                 intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
    1620             :                 "locale_accept_from_http: unable to parse input parameters", 0 );
    1621           3 :                 RETURN_FALSE;
    1622             :         }
    1623          14 :         if(http_accept_len > ULOC_FULLNAME_CAPACITY) {
    1624             :                 /* check each fragment, if any bigger than capacity, can't do it due to bug #72533 */
    1625           4 :                 char *start = http_accept;
    1626             :                 char *end;
    1627             :                 size_t len;
    1628             :                 do {
    1629         516 :                         end = strchr(start, ',');
    1630         516 :                         len = end ? end-start : http_accept_len-(start-http_accept);
    1631         516 :                         if(len > ULOC_FULLNAME_CAPACITY) {
    1632           2 :                                 intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
    1633             :                                                 "locale_accept_from_http: locale string too long", 0 );
    1634           2 :                                 RETURN_FALSE;
    1635             :                         }
    1636         514 :                         if(end) {
    1637         512 :                                 start = end+1;
    1638             :                         }
    1639         514 :                 } while(end != NULL);
    1640             :         }
    1641             : 
    1642          12 :         available = ures_openAvailableLocales(NULL, &status);
    1643          12 :         INTL_CHECK_STATUS(status, "locale_accept_from_http: failed to retrieve locale list");
    1644          12 :         len = uloc_acceptLanguageFromHTTP(resultLocale, INTL_MAX_LOCALE_LEN,
    1645             :                                                 &outResult, http_accept, available, &status);
    1646          12 :         uenum_close(available);
    1647          12 :         INTL_CHECK_STATUS(status, "locale_accept_from_http: failed to find acceptable locale");
    1648          12 :         if (len < 0 || outResult == ULOC_ACCEPT_FAILED) {
    1649           2 :                 RETURN_FALSE;
    1650             :         }
    1651          20 :         RETURN_STRINGL(resultLocale, len);
    1652             : }
    1653             : /* }}} */
    1654             : 
    1655             : /*
    1656             :  * Local variables:
    1657             :  * tab-width: 4
    1658             :  * c-basic-offset: 4
    1659             :  * End:
    1660             :  * vim600: noet sw=4 ts=4 fdm=marker
    1661             :  * vim<600: noet sw=4 ts=4
    1662             :  *can_loc_len
    1663             : */

Generated by: LCOV version 1.10

Generated at Fri, 21 Jan 2022 14:15:12 +0000 (31 hours ago)

Copyright © 2005-2022 The PHP Group
All rights reserved.