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