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