php-src/ext/intl/locale/locale_methods.c
Peter Kokot 92ac598aab Remove local variables
This patch removes the so called local variables defined per
file basis for certain editors to properly show tab width, and
similar settings. These are mainly used by Vim and Emacs editors
yet with recent changes the once working definitions don't work
anymore in Vim without custom plugins or additional configuration.
Neither are these settings synced across the PHP code base.

A simpler and better approach is EditorConfig and fixing code
using some code style fixing tools in the future instead.

This patch also removes the so called modelines for Vim. Modelines
allow Vim editor specifically to set some editor configuration such as
syntax highlighting, indentation style and tab width to be set in the
first line or the last 5 lines per file basis. Since the php test
files have syntax highlighting already set in most editors properly and
EditorConfig takes care of the indentation settings, this patch removes
these as well for the Vim 6.0 and newer versions.

With the removal of local variables for certain editors such as
Emacs and Vim, the footer is also probably not needed anymore when
creating extensions using ext_skel.php script.

Additionally, Vim modelines for setting php syntax and some editor
settings has been removed from some *.phpt files.  All these are
mostly not relevant for phpt files neither work properly in the
middle of the file.
2019-02-03 21:03:00 +01:00

1631 lines
46 KiB
C
Raw Blame History

/*
+----------------------------------------------------------------------+
| PHP Version 7 |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Kirti Velankar <kirtig@yahoo-inc.com> |
+----------------------------------------------------------------------+
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <unicode/ustring.h>
#include <unicode/udata.h>
#include <unicode/putil.h>
#include <unicode/ures.h>
#include "php_intl.h"
#include "locale.h"
#include "locale_class.h"
#include "locale_methods.h"
#include "intl_convert.h"
#include "intl_data.h"
#include <zend_API.h>
#include <zend.h>
#include <php.h>
#include "main/php_ini.h"
#include "zend_smart_str.h"
ZEND_EXTERN_MODULE_GLOBALS( intl )
/* Sizes required for the strings "variant15" , "extlang11", "private12" etc. */
#define SEPARATOR "_"
#define SEPARATOR1 "-"
#define DELIMITER "-_"
#define EXTLANG_PREFIX "a"
#define PRIVATE_PREFIX "x"
#define DISP_NAME "name"
#define MAX_NO_VARIANT 15
#define MAX_NO_EXTLANG 3
#define MAX_NO_PRIVATE 15
#define MAX_NO_LOOKUP_LANG_TAG 100
#define LOC_NOT_FOUND 1
/* Sizes required for the strings "variant15" , "extlang3", "private12" etc. */
#define VARIANT_KEYNAME_LEN 11
#define EXTLANG_KEYNAME_LEN 10
#define PRIVATE_KEYNAME_LEN 11
/* Based on IANA registry at the time of writing this code
*
*/
static const char * const LOC_GRANDFATHERED[] = {
"art-lojban", "i-klingon", "i-lux", "i-navajo", "no-bok", "no-nyn",
"cel-gaulish", "en-GB-oed", "i-ami",
"i-bnn", "i-default", "i-enochian",
"i-mingo", "i-pwn", "i-tao",
"i-tay", "i-tsu", "sgn-BE-fr",
"sgn-BE-nl", "sgn-CH-de", "zh-cmn",
"zh-cmn-Hans", "zh-cmn-Hant", "zh-gan" ,
"zh-guoyu", "zh-hakka", "zh-min",
"zh-min-nan", "zh-wuu", "zh-xiang",
"zh-yue", NULL
};
/* Based on IANA registry at the time of writing this code
* This array lists the preferred values for the grandfathered tags if applicable
* This is in sync with the array LOC_GRANDFATHERED
* e.g. the offsets of the grandfathered tags match the offset of the preferred value
*/
static const int LOC_PREFERRED_GRANDFATHERED_LEN = 6;
static const char * const LOC_PREFERRED_GRANDFATHERED[] = {
"jbo", "tlh", "lb",
"nv", "nb", "nn",
NULL
};
/*returns TRUE if a is an ID separator FALSE otherwise*/
#define isIDSeparator(a) (a == '_' || a == '-')
#define isKeywordSeparator(a) (a == '@' )
#define isEndOfTag(a) (a == '\0' )
#define isPrefixLetter(a) ((a=='x')||(a=='X')||(a=='i')||(a=='I'))
/*returns TRUE if one of the special prefixes is here (s=string)
'x-' or 'i-' */
#define isIDPrefix(s) (isPrefixLetter(s[0])&&isIDSeparator(s[1]))
#define isKeywordPrefix(s) ( isKeywordSeparator(s[0]) )
/* Dot terminates it because of POSIX form where dot precedes the codepage
* except for variant */
#define isTerminator(a) ((a==0)||(a=='.')||(a=='@'))
/* {{{ return the offset of 'key' in the array 'list'.
* returns -1 if not present */
static int16_t findOffset(const char* const* list, const char* key)
{
const char* const* anchor = list;
while (*list != NULL) {
if (strcmp(key, *list) == 0) {
return (int16_t)(list - anchor);
}
list++;
}
return -1;
}
/*}}}*/
static char* getPreferredTag(const char* gf_tag)
{
char* result = NULL;
zend_off_t grOffset = 0;
grOffset = findOffset( LOC_GRANDFATHERED ,gf_tag);
if(grOffset < 0) {
return NULL;
}
if( grOffset < LOC_PREFERRED_GRANDFATHERED_LEN ){
/* return preferred tag */
result = estrdup( LOC_PREFERRED_GRANDFATHERED[grOffset] );
} else {
/* Return correct grandfathered language tag */
result = estrdup( LOC_GRANDFATHERED[grOffset] );
}
return result;
}
/* {{{
* returns the position of next token for lookup
* or -1 if no token
* strtokr equivalent search for token in reverse direction
*/
static zend_off_t getStrrtokenPos(char* str, zend_off_t savedPos)
{
zend_off_t result =-1;
zend_off_t i;
for(i=savedPos-1; i>=0; i--) {
if(isIDSeparator(*(str+i)) ){
/* delimiter found; check for singleton */
if(i>=2 && isIDSeparator(*(str+i-2)) ){
/* a singleton; so send the position of token before the singleton */
result = i-2;
} else {
result = i;
}
break;
}
}
if(result < 1){
/* Just in case inavlid locale e.g. '-x-xyz' or '-sl_Latn' */
result =-1;
}
return result;
}
/* }}} */
/* {{{
* returns the position of a singleton if present
* returns -1 if no singleton
* strtok equivalent search for singleton
*/
static zend_off_t getSingletonPos(const char* str)
{
zend_off_t result =-1;
size_t len = 0;
if( str && ((len=strlen(str))>0) ){
zend_off_t i = 0;
for( i=0; (size_t)i < len ; i++){
if( isIDSeparator(*(str+i)) ){
if( i==1){
/* string is of the form x-avy or a-prv1 */
result =0;
break;
} else {
/* delimiter found; check for singleton */
if( isIDSeparator(*(str+i+2)) ){
/* a singleton; so send the position of separator before singleton */
result = i+1;
break;
}
}
}
}/* end of for */
}
return result;
}
/* }}} */
/* {{{ proto static string Locale::getDefault( )
Get default locale */
/* }}} */
/* {{{ proto static string locale_get_default( )
Get default locale */
PHP_NAMED_FUNCTION(zif_locale_get_default)
{
RETURN_STRING( intl_locale_get_default( ) );
}
/* }}} */
/* {{{ proto static string Locale::setDefault( string $locale )
Set default locale */
/* }}} */
/* {{{ proto static string locale_set_default( string $locale )
Set default locale */
PHP_NAMED_FUNCTION(zif_locale_set_default)
{
zend_string* locale_name;
zend_string *ini_name;
char *default_locale = NULL;
if(zend_parse_parameters( ZEND_NUM_ARGS(), "S", &locale_name) == FAILURE)
{
intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
"locale_set_default: unable to parse input params", 0 );
RETURN_FALSE;
}
if (ZSTR_LEN(locale_name) == 0) {
default_locale = (char *)uloc_getDefault();
locale_name = zend_string_init(default_locale, strlen(default_locale), 0);
}
ini_name = zend_string_init(LOCALE_INI_NAME, sizeof(LOCALE_INI_NAME) - 1, 0);
zend_alter_ini_entry(ini_name, locale_name, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
zend_string_release_ex(ini_name, 0);
if (default_locale != NULL) {
zend_string_release_ex(locale_name, 0);
}
RETURN_TRUE;
}
/* }}} */
/* {{{
* Gets the value from ICU
* common code shared by get_primary_language,get_script or get_region or get_variant
* result = 0 if error, 1 if successful , -1 if no value
*/
static zend_string* get_icu_value_internal( const char* loc_name , char* tag_name, int* result , int fromParseLocale)
{
zend_string* tag_value = NULL;
int32_t tag_value_len = 512;
char* mod_loc_name = NULL;
int32_t buflen = 512;
UErrorCode status = U_ZERO_ERROR;
if (strlen(loc_name) > INTL_MAX_LOCALE_LEN) {
return NULL;
}
if( strcmp(tag_name, LOC_CANONICALIZE_TAG) != 0 ){
/* Handle grandfathered languages */
zend_off_t grOffset = findOffset( LOC_GRANDFATHERED , loc_name );
if( grOffset >= 0 ){
if( strcmp(tag_name , LOC_LANG_TAG)==0 ){
return zend_string_init(loc_name, strlen(loc_name), 0);
} else {
/* Since Grandfathered , no value , do nothing , retutn NULL */
return NULL;
}
}
if( fromParseLocale==1 ){
zend_off_t singletonPos = 0;
/* Handle singletons */
if( strcmp(tag_name , LOC_LANG_TAG)==0 ){
if( strlen(loc_name)>1 && (isIDPrefix(loc_name) == 1) ){
return zend_string_init(loc_name, strlen(loc_name), 0);
}
}
singletonPos = getSingletonPos( loc_name );
if( singletonPos == 0){
/* singleton at start of script, region , variant etc.
* or invalid singleton at start of language */
return NULL;
} else if( singletonPos > 0 ){
/* singleton at some position except at start
* strip off the singleton and rest of the loc_name */
mod_loc_name = estrndup ( loc_name , singletonPos-1);
}
} /* end of if fromParse */
} /* end of if != LOC_CANONICAL_TAG */
if( mod_loc_name == NULL){
mod_loc_name = estrdup(loc_name );
}
/* Proceed to ICU */
do{
if (tag_value) {
tag_value = zend_string_realloc( tag_value , buflen, 0);
} else {
tag_value = zend_string_alloc( buflen, 0);
}
tag_value_len = buflen;
if( strcmp(tag_name , LOC_SCRIPT_TAG)==0 ){
buflen = uloc_getScript ( mod_loc_name , tag_value->val , tag_value_len , &status);
}
if( strcmp(tag_name , LOC_LANG_TAG )==0 ){
buflen = uloc_getLanguage ( mod_loc_name , tag_value->val , tag_value_len , &status);
}
if( strcmp(tag_name , LOC_REGION_TAG)==0 ){
buflen = uloc_getCountry ( mod_loc_name , tag_value->val , tag_value_len , &status);
}
if( strcmp(tag_name , LOC_VARIANT_TAG)==0 ){
buflen = uloc_getVariant ( mod_loc_name , tag_value->val , tag_value_len , &status);
}
if( strcmp(tag_name , LOC_CANONICALIZE_TAG)==0 ){
buflen = uloc_canonicalize ( mod_loc_name , tag_value->val , tag_value_len , &status);
}
if( U_FAILURE( status ) ) {
if( status == U_BUFFER_OVERFLOW_ERROR ) {
status = U_ZERO_ERROR;
buflen++; /* add space for \0 */
continue;
}
/* Error in retrieving data */
*result = 0;
if( tag_value ){
zend_string_release_ex( tag_value, 0 );
}
if( mod_loc_name ){
efree( mod_loc_name);
}
return NULL;
}
} while( buflen > tag_value_len );
if( buflen ==0 ){
/* No value found */
*result = -1;
if( tag_value ){
zend_string_release_ex( tag_value, 0 );
}
if( mod_loc_name ){
efree( mod_loc_name);
}
return NULL;
} else {
*result = 1;
}
if( mod_loc_name ){
efree( mod_loc_name);
}
tag_value->len = strlen(tag_value->val);
return tag_value;
}
/* }}} */
/* {{{
* Gets the value from ICU , called when PHP userspace function is called
* common code shared by get_primary_language,get_script or get_region or get_variant
*/
static void get_icu_value_src_php( char* tag_name, INTERNAL_FUNCTION_PARAMETERS)
{
const char* loc_name = NULL;
size_t loc_name_len = 0;
zend_string* tag_value = NULL;
char* empty_result = "";
int result = 0;
char* msg = NULL;
UErrorCode status = U_ZERO_ERROR;
intl_error_reset( NULL );
if(zend_parse_parameters( ZEND_NUM_ARGS(), "s",
&loc_name ,&loc_name_len ) == FAILURE) {
spprintf(&msg , 0, "locale_get_%s : unable to parse input params", tag_name );
intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, msg , 1 );
efree(msg);
RETURN_FALSE;
}
if(loc_name_len == 0) {
loc_name = intl_locale_get_default();
loc_name_len = strlen(loc_name);
}
INTL_CHECK_LOCALE_LEN(loc_name_len);
/* Call ICU get */
tag_value = get_icu_value_internal( loc_name , tag_name , &result ,0);
/* No value found */
if( result == -1 ) {
if( tag_value){
zend_string_release_ex( tag_value, 0 );
}
RETURN_STRING( empty_result);
}
/* value found */
if( tag_value){
RETVAL_STR( tag_value );
return;
}
/* Error encountered while fetching the value */
if( result ==0) {
spprintf(&msg , 0, "locale_get_%s : unable to get locale %s", tag_name , tag_name );
intl_error_set( NULL, status, msg , 1 );
efree(msg);
RETURN_NULL();
}
}
/* }}} */
/* {{{ proto static string Locale::getScript($locale)
* gets the script for the $locale
}}} */
/* {{{ proto static string locale_get_script($locale)
* gets the script for the $locale
*/
PHP_FUNCTION( locale_get_script )
{
get_icu_value_src_php( LOC_SCRIPT_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
}
/* }}} */
/* {{{ proto static string Locale::getRegion($locale)
* gets the region for the $locale
}}} */
/* {{{ proto static string locale_get_region($locale)
* gets the region for the $locale
*/
PHP_FUNCTION( locale_get_region )
{
get_icu_value_src_php( LOC_REGION_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
}
/* }}} */
/* {{{ proto static string Locale::getPrimaryLanguage($locale)
* gets the primary language for the $locale
}}} */
/* {{{ proto static string locale_get_primary_language($locale)
* gets the primary language for the $locale
*/
PHP_FUNCTION(locale_get_primary_language )
{
get_icu_value_src_php( LOC_LANG_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
}
/* }}} */
/* {{{
* common code shared by display_xyz functions to get the value from ICU
}}} */
static void get_icu_disp_value_src_php( char* tag_name, INTERNAL_FUNCTION_PARAMETERS)
{
const char* loc_name = NULL;
size_t loc_name_len = 0;
const char* disp_loc_name = NULL;
size_t disp_loc_name_len = 0;
int free_loc_name = 0;
UChar* disp_name = NULL;
int32_t disp_name_len = 0;
char* mod_loc_name = NULL;
int32_t buflen = 512;
UErrorCode status = U_ZERO_ERROR;
zend_string* u8str;
char* msg = NULL;
intl_error_reset( NULL );
if(zend_parse_parameters( ZEND_NUM_ARGS(), "s|s",
&loc_name, &loc_name_len ,
&disp_loc_name ,&disp_loc_name_len ) == FAILURE)
{
spprintf(&msg , 0, "locale_get_display_%s : unable to parse input params", tag_name );
intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, msg , 1 );
efree(msg);
RETURN_FALSE;
}
if(loc_name_len > ULOC_FULLNAME_CAPACITY) {
/* See bug 67397: overlong locale names cause trouble in uloc_getDisplayName */
spprintf(&msg , 0, "locale_get_display_%s : name too long", tag_name );
intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, msg , 1 );
efree(msg);
RETURN_FALSE;
}
if(loc_name_len == 0) {
loc_name = intl_locale_get_default();
}
if( strcmp(tag_name, DISP_NAME) != 0 ){
/* Handle grandfathered languages */
int grOffset = findOffset( LOC_GRANDFATHERED , loc_name );
if( grOffset >= 0 ){
if( strcmp(tag_name , LOC_LANG_TAG)==0 ){
mod_loc_name = getPreferredTag( loc_name );
} else {
/* Since Grandfathered, no value, do nothing, retutn NULL */
RETURN_FALSE;
}
}
} /* end of if != LOC_CANONICAL_TAG */
if( mod_loc_name==NULL ){
mod_loc_name = estrdup( loc_name );
}
/* Check if disp_loc_name passed , if not use default locale */
if( !disp_loc_name){
disp_loc_name = estrdup(intl_locale_get_default());
free_loc_name = 1;
}
/* Get the disp_value for the given locale */
do{
disp_name = erealloc( disp_name , buflen * sizeof(UChar) );
disp_name_len = buflen;
if( strcmp(tag_name , LOC_LANG_TAG)==0 ){
buflen = uloc_getDisplayLanguage ( mod_loc_name , disp_loc_name , disp_name , disp_name_len , &status);
} else if( strcmp(tag_name , LOC_SCRIPT_TAG)==0 ){
buflen = uloc_getDisplayScript ( mod_loc_name , disp_loc_name , disp_name , disp_name_len , &status);
} else if( strcmp(tag_name , LOC_REGION_TAG)==0 ){
buflen = uloc_getDisplayCountry ( mod_loc_name , disp_loc_name , disp_name , disp_name_len , &status);
} else if( strcmp(tag_name , LOC_VARIANT_TAG)==0 ){
buflen = uloc_getDisplayVariant ( mod_loc_name , disp_loc_name , disp_name , disp_name_len , &status);
} else if( strcmp(tag_name , DISP_NAME)==0 ){
buflen = uloc_getDisplayName ( mod_loc_name , disp_loc_name , disp_name , disp_name_len , &status);
}
/* U_STRING_NOT_TERMINATED_WARNING is admissible here; don't look for it */
if( U_FAILURE( status ) )
{
if( status == U_BUFFER_OVERFLOW_ERROR )
{
status = U_ZERO_ERROR;
continue;
}
spprintf(&msg, 0, "locale_get_display_%s : unable to get locale %s", tag_name , tag_name );
intl_error_set( NULL, status, msg , 1 );
efree(msg);
if( disp_name){
efree( disp_name );
}
if( mod_loc_name){
efree( mod_loc_name );
}
if (free_loc_name) {
efree((void *)disp_loc_name);
disp_loc_name = NULL;
}
RETURN_FALSE;
}
} while( buflen > disp_name_len );
if( mod_loc_name){
efree( mod_loc_name );
}
if (free_loc_name) {
efree((void *)disp_loc_name);
disp_loc_name = NULL;
}
/* Convert display locale name from UTF-16 to UTF-8. */
u8str = intl_convert_utf16_to_utf8(disp_name, buflen, &status );
efree( disp_name );
if( !u8str )
{
spprintf(&msg, 0, "locale_get_display_%s :error converting display name for %s to UTF-8", tag_name , tag_name );
intl_error_set( NULL, status, msg , 1 );
efree(msg);
RETURN_FALSE;
}
RETVAL_NEW_STR( u8str );
}
/* }}} */
/* {{{ proto static string Locale::getDisplayName($locale[, $in_locale = null])
* gets the name for the $locale in $in_locale or default_locale
}}} */
/* {{{ proto static string get_display_name($locale[, $in_locale = null])
* gets the name for the $locale in $in_locale or default_locale
*/
PHP_FUNCTION(locale_get_display_name)
{
get_icu_disp_value_src_php( DISP_NAME , INTERNAL_FUNCTION_PARAM_PASSTHRU );
}
/* }}} */
/* {{{ proto static string Locale::getDisplayLanguage($locale[, $in_locale = null])
* gets the language for the $locale in $in_locale or default_locale
}}} */
/* {{{ proto static string get_display_language($locale[, $in_locale = null])
* gets the language for the $locale in $in_locale or default_locale
*/
PHP_FUNCTION(locale_get_display_language)
{
get_icu_disp_value_src_php( LOC_LANG_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
}
/* }}} */
/* {{{ proto static string Locale::getDisplayScript($locale, $in_locale = null)
* gets the script for the $locale in $in_locale or default_locale
}}} */
/* {{{ proto static string get_display_script($locale, $in_locale = null)
* gets the script for the $locale in $in_locale or default_locale
*/
PHP_FUNCTION(locale_get_display_script)
{
get_icu_disp_value_src_php( LOC_SCRIPT_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
}
/* }}} */
/* {{{ proto static string Locale::getDisplayRegion($locale, $in_locale = null)
* gets the region for the $locale in $in_locale or default_locale
}}} */
/* {{{ proto static string get_display_region($locale, $in_locale = null)
* gets the region for the $locale in $in_locale or default_locale
*/
PHP_FUNCTION(locale_get_display_region)
{
get_icu_disp_value_src_php( LOC_REGION_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
}
/* }}} */
/* {{{
* proto static string Locale::getDisplayVariant($locale, $in_locale = null)
* gets the variant for the $locale in $in_locale or default_locale
}}} */
/* {{{
* proto static string get_display_variant($locale, $in_locale = null)
* gets the variant for the $locale in $in_locale or default_locale
*/
PHP_FUNCTION(locale_get_display_variant)
{
get_icu_disp_value_src_php( LOC_VARIANT_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
}
/* }}} */
/* {{{ proto static array getKeywords(string $locale) {
* return an associative array containing keyword-value
* pairs for this locale. The keys are keys to the array (doh!)
* }}}*/
/* {{{ proto static array locale_get_keywords(string $locale) {
* return an associative array containing keyword-value
* pairs for this locale. The keys are keys to the array (doh!)
*/
PHP_FUNCTION( locale_get_keywords )
{
UEnumeration* e = NULL;
UErrorCode status = U_ZERO_ERROR;
const char* kw_key = NULL;
int32_t kw_key_len = 0;
const char* loc_name = NULL;
size_t loc_name_len = 0;
intl_error_reset( NULL );
if(zend_parse_parameters( ZEND_NUM_ARGS(), "s",
&loc_name, &loc_name_len ) == FAILURE)
{
intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
"locale_get_keywords: unable to parse input params", 0 );
RETURN_FALSE;
}
INTL_CHECK_LOCALE_LEN(strlen(loc_name));
if(loc_name_len == 0) {
loc_name = intl_locale_get_default();
}
/* Get the keywords */
e = uloc_openKeywords( loc_name, &status );
if( e != NULL )
{
/*
ICU expects the buffer to be allocated before calling the function
and so the buffer size has been explicitly specified
ICU uloc.h #define ULOC_KEYWORD_AND_VALUES_CAPACITY 100
hence the kw_value buffer size is 100
*/
/* Traverse it, filling the return array. */
array_init( return_value );
while( ( kw_key = uenum_next( e, &kw_key_len, &status ) ) != NULL ){
int32_t kw_value_len = 100;
zend_string *kw_value_str = zend_string_alloc(kw_value_len, 0);
/* Get the keyword value for each keyword */
kw_value_len=uloc_getKeywordValue( loc_name, kw_key, ZSTR_VAL(kw_value_str), kw_value_len, &status );
if (status == U_BUFFER_OVERFLOW_ERROR) {
status = U_ZERO_ERROR;
kw_value_str = zend_string_extend(kw_value_str, kw_value_len, 0);
kw_value_len=uloc_getKeywordValue( loc_name,kw_key, ZSTR_VAL(kw_value_str), kw_value_len+1, &status );
} else if(!U_FAILURE(status)) {
kw_value_str = zend_string_truncate(kw_value_str, kw_value_len, 0);
}
if (U_FAILURE(status)) {
intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "locale_get_keywords: Error encountered while getting the keyword value for the keyword", 0 );
if( kw_value_str){
zend_string_efree( kw_value_str );
}
zend_array_destroy(Z_ARR_P(return_value));
RETURN_FALSE;
}
add_assoc_str( return_value, (char *)kw_key, kw_value_str);
} /* end of while */
} /* end of if e!=NULL */
uenum_close( e );
}
/* }}} */
/* {{{ proto static string Locale::canonicalize($locale)
* @return string the canonicalized locale
* }}} */
/* {{{ proto static string locale_canonicalize(Locale $loc, string $locale)
* @param string $locale The locale string to canonicalize
*/
PHP_FUNCTION(locale_canonicalize)
{
get_icu_value_src_php( LOC_CANONICALIZE_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
}
/* }}} */
/* {{{ append_key_value
* Internal function which is called from locale_compose
* gets the value for the key_name and appends to the loc_name
* returns 1 if successful , -1 if not found ,
* 0 if array element is not a string , -2 if buffer-overflow
*/
static int append_key_value(smart_str* loc_name, HashTable* hash_arr, char* key_name)
{
zval *ele_value;
if ((ele_value = zend_hash_str_find(hash_arr , key_name, strlen(key_name))) != NULL ) {
if(Z_TYPE_P(ele_value)!= IS_STRING ){
/* element value is not a string */
return FAILURE;
}
if(strcmp(key_name, LOC_LANG_TAG) != 0 &&
strcmp(key_name, LOC_GRANDFATHERED_LANG_TAG)!=0 ) {
/* not lang or grandfathered tag */
smart_str_appendl(loc_name, SEPARATOR , sizeof(SEPARATOR)-1);
}
smart_str_appendl(loc_name, Z_STRVAL_P(ele_value) , Z_STRLEN_P(ele_value));
return SUCCESS;
}
return LOC_NOT_FOUND;
}
/* }}} */
/* {{{ append_prefix , appends the prefix needed
* e.g. private adds 'x'
*/
static void add_prefix(smart_str* loc_name, char* key_name)
{
if( strncmp(key_name , LOC_PRIVATE_TAG , 7) == 0 ){
smart_str_appendl(loc_name, SEPARATOR , sizeof(SEPARATOR)-1);
smart_str_appendl(loc_name, PRIVATE_PREFIX , sizeof(PRIVATE_PREFIX)-1);
}
}
/* }}} */
/* {{{ append_multiple_key_values
* Internal function which is called from locale_compose
* gets the multiple values for the key_name and appends to the loc_name
* used for 'variant','extlang','private'
* returns 1 if successful , -1 if not found ,
* 0 if array element is not a string , -2 if buffer-overflow
*/
static int append_multiple_key_values(smart_str* loc_name, HashTable* hash_arr, char* key_name)
{
zval *ele_value;
int isFirstSubtag = 0;
/* Variant/ Extlang/Private etc. */
if ((ele_value = zend_hash_str_find( hash_arr , key_name , strlen(key_name))) != NULL) {
if( Z_TYPE_P(ele_value) == IS_STRING ){
add_prefix( loc_name , key_name);
smart_str_appendl(loc_name, SEPARATOR , sizeof(SEPARATOR)-1);
smart_str_appendl(loc_name, Z_STRVAL_P(ele_value) , Z_STRLEN_P(ele_value));
return SUCCESS;
} else if(Z_TYPE_P(ele_value) == IS_ARRAY ) {
HashTable *arr = Z_ARRVAL_P(ele_value);
zval *data;
ZEND_HASH_FOREACH_VAL(arr, data) {
if(Z_TYPE_P(data) != IS_STRING) {
return FAILURE;
}
if (isFirstSubtag++ == 0){
add_prefix(loc_name , key_name);
}
smart_str_appendl(loc_name, SEPARATOR , sizeof(SEPARATOR)-1);
smart_str_appendl(loc_name, Z_STRVAL_P(data) , Z_STRLEN_P(data));
} ZEND_HASH_FOREACH_END();
return SUCCESS;
} else {
return FAILURE;
}
} else {
char cur_key_name[31];
int max_value = 0, i;
/* Decide the max_value: the max. no. of elements allowed */
if( strcmp(key_name , LOC_VARIANT_TAG) ==0 ){
max_value = MAX_NO_VARIANT;
}
if( strcmp(key_name , LOC_EXTLANG_TAG) ==0 ){
max_value = MAX_NO_EXTLANG;
}
if( strcmp(key_name , LOC_PRIVATE_TAG) ==0 ){
max_value = MAX_NO_PRIVATE;
}
/* Multiple variant values as variant0, variant1 ,variant2 */
isFirstSubtag = 0;
for( i=0 ; i< max_value; i++ ){
snprintf( cur_key_name , 30, "%s%d", key_name , i);
if ((ele_value = zend_hash_str_find( hash_arr , cur_key_name , strlen(cur_key_name))) != NULL) {
if( Z_TYPE_P(ele_value)!= IS_STRING ){
/* variant is not a string */
return FAILURE;
}
/* Add the contents */
if (isFirstSubtag++ == 0){
add_prefix(loc_name , cur_key_name);
}
smart_str_appendl(loc_name, SEPARATOR , sizeof(SEPARATOR)-1);
smart_str_appendl(loc_name, Z_STRVAL_P(ele_value) , Z_STRLEN_P(ele_value));
}
} /* end of for */
} /* end of else */
return SUCCESS;
}
/* }}} */
/*{{{
* If applicable sets error message and aborts locale_compose gracefully
* returns 0 if locale_compose needs to be aborted
* otherwise returns 1
*/
static int handleAppendResult( int result, smart_str* loc_name)
{
intl_error_reset( NULL );
if( result == FAILURE) {
intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
"locale_compose: parameter array element is not a string", 0 );
smart_str_free(loc_name);
return 0;
}
return 1;
}
/* }}} */
#define RETURN_SMART_STR(str) smart_str_0((str)); RETURN_NEW_STR((str)->s)
/* {{{ proto static string Locale::composeLocale($array)
* Creates a locale by combining the parts of locale-ID passed
* }}} */
/* {{{ proto static string compose_locale($array)
* Creates a locale by combining the parts of locale-ID passed
* }}} */
PHP_FUNCTION(locale_compose)
{
smart_str loc_name_s = {0};
smart_str *loc_name = &loc_name_s;
zval* arr = NULL;
HashTable* hash_arr = NULL;
int result = 0;
intl_error_reset( NULL );
if(zend_parse_parameters( ZEND_NUM_ARGS(), "a",
&arr) == FAILURE)
{
intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
"locale_compose: unable to parse input params", 0 );
RETURN_FALSE;
}
hash_arr = Z_ARRVAL_P( arr );
if( !hash_arr || zend_hash_num_elements( hash_arr ) == 0 )
RETURN_FALSE;
/* Check for grandfathered first */
result = append_key_value(loc_name, hash_arr, LOC_GRANDFATHERED_LANG_TAG);
if( result == SUCCESS){
RETURN_SMART_STR(loc_name);
}
if( !handleAppendResult( result, loc_name)){
RETURN_FALSE;
}
/* Not grandfathered */
result = append_key_value(loc_name, hash_arr , LOC_LANG_TAG);
if( result == LOC_NOT_FOUND ){
intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
"locale_compose: parameter array does not contain 'language' tag.", 0 );
smart_str_free(loc_name);
RETURN_FALSE;
}
if( !handleAppendResult( result, loc_name)){
RETURN_FALSE;
}
/* Extlang */
result = append_multiple_key_values(loc_name, hash_arr , LOC_EXTLANG_TAG);
if( !handleAppendResult( result, loc_name)){
RETURN_FALSE;
}
/* Script */
result = append_key_value(loc_name, hash_arr , LOC_SCRIPT_TAG);
if( !handleAppendResult( result, loc_name)){
RETURN_FALSE;
}
/* Region */
result = append_key_value( loc_name, hash_arr , LOC_REGION_TAG);
if( !handleAppendResult( result, loc_name)){
RETURN_FALSE;
}
/* Variant */
result = append_multiple_key_values( loc_name, hash_arr , LOC_VARIANT_TAG);
if( !handleAppendResult( result, loc_name)){
RETURN_FALSE;
}
/* Private */
result = append_multiple_key_values( loc_name, hash_arr , LOC_PRIVATE_TAG);
if( !handleAppendResult( result, loc_name)){
RETURN_FALSE;
}
RETURN_SMART_STR(loc_name);
}
/* }}} */
/*{{{
* Parses the locale and returns private subtags if existing
* else returns NULL
* e.g. for locale='en_US-x-prv1-prv2-prv3'
* returns a pointer to the string 'prv1-prv2-prv3'
*/
static zend_string* get_private_subtags(const char* loc_name)
{
zend_string* result = NULL;
size_t len = 0;
const char* mod_loc_name =NULL;
if( loc_name && (len = strlen(loc_name)) > 0 ){
zend_off_t singletonPos = 0;
mod_loc_name = loc_name ;
while( (singletonPos = getSingletonPos(mod_loc_name)) > -1){
if( (*(mod_loc_name+singletonPos)=='x') || (*(mod_loc_name+singletonPos)=='X') ){
/* private subtag start found */
if( singletonPos + 2 == len){
/* loc_name ends with '-x-' ; return NULL */
}
else{
/* result = mod_loc_name + singletonPos +2; */
result = zend_string_init(mod_loc_name + singletonPos+2 , (len -( singletonPos +2) ), 0);
}
break;
}
else{
if((size_t)(singletonPos + 1) >= len){
/* String end */
break;
} else {
/* singleton found but not a private subtag , hence check further in the string for the private subtag */
mod_loc_name = mod_loc_name + singletonPos +1;
len = strlen(mod_loc_name);
}
}
} /* end of while */
}
return result;
}
/* }}} */
/* {{{ code used by locale_parse
*/
static int add_array_entry(const char* loc_name, zval* hash_arr, char* key_name)
{
zend_string* key_value = NULL;
char* cur_key_name = NULL;
char* token = NULL;
char* last_ptr = NULL;
int result = 0;
int cur_result = 0;
if( strcmp(key_name , LOC_PRIVATE_TAG)==0 ){
key_value = get_private_subtags( loc_name );
result = 1;
} else {
key_value = get_icu_value_internal( loc_name , key_name , &result,1 );
}
if( (strcmp(key_name , LOC_PRIVATE_TAG)==0) ||
( strcmp(key_name , LOC_VARIANT_TAG)==0) ){
if( result > 0 && key_value){
int cnt = 0;
/* Tokenize on the "_" or "-" */
token = php_strtok_r( key_value->val , DELIMITER ,&last_ptr);
if( cur_key_name ){
efree( cur_key_name);
}
cur_key_name = (char*)ecalloc( 25, 25);
sprintf( cur_key_name , "%s%d", key_name , cnt++);
add_assoc_string( hash_arr, cur_key_name , token);
/* tokenize on the "_" or "-" and stop at singleton if any */
while( (token = php_strtok_r(NULL , DELIMITER , &last_ptr)) && (strlen(token)>1) ){
sprintf( cur_key_name , "%s%d", key_name , cnt++);
add_assoc_string( hash_arr, cur_key_name , token);
}
/*
if( strcmp(key_name, LOC_PRIVATE_TAG) == 0 ){
}
*/
}
if (key_value) {
zend_string_release_ex(key_value, 0);
}
} else {
if( result == 1 ){
add_assoc_str( hash_arr, key_name , key_value);
cur_result = 1;
} else if (key_value) {
zend_string_release_ex(key_value, 0);
}
}
if( cur_key_name ){
efree( cur_key_name);
}
/*if( key_name != LOC_PRIVATE_TAG && key_value){*/
return cur_result;
}
/* }}} */
/* {{{ proto static array Locale::parseLocale($locale)
* parses a locale-id into an array the different parts of it
}}} */
/* {{{ proto static array parse_locale($locale)
* parses a locale-id into an array the different parts of it
*/
PHP_FUNCTION(locale_parse)
{
const char* loc_name = NULL;
size_t loc_name_len = 0;
int grOffset = 0;
intl_error_reset( NULL );
if(zend_parse_parameters( ZEND_NUM_ARGS(), "s",
&loc_name, &loc_name_len ) == FAILURE)
{
intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
"locale_parse: unable to parse input params", 0 );
RETURN_FALSE;
}
INTL_CHECK_LOCALE_LEN(strlen(loc_name));
if(loc_name_len == 0) {
loc_name = intl_locale_get_default();
}
array_init( return_value );
grOffset = findOffset( LOC_GRANDFATHERED , loc_name );
if( grOffset >= 0 ){
add_assoc_string( return_value , LOC_GRANDFATHERED_LANG_TAG, (char *)loc_name);
}
else{
/* Not grandfathered */
add_array_entry( loc_name , return_value , LOC_LANG_TAG);
add_array_entry( loc_name , return_value , LOC_SCRIPT_TAG);
add_array_entry( loc_name , return_value , LOC_REGION_TAG);
add_array_entry( loc_name , return_value , LOC_VARIANT_TAG);
add_array_entry( loc_name , return_value , LOC_PRIVATE_TAG);
}
}
/* }}} */
/* {{{ proto static array Locale::getAllVariants($locale)
* gets an array containing the list of variants, or null
}}} */
/* {{{ proto static array locale_get_all_variants($locale)
* gets an array containing the list of variants, or null
*/
PHP_FUNCTION(locale_get_all_variants)
{
const char* loc_name = NULL;
size_t loc_name_len = 0;
int result = 0;
char* token = NULL;
zend_string* variant = NULL;
char* saved_ptr = NULL;
intl_error_reset( NULL );
if(zend_parse_parameters( ZEND_NUM_ARGS(), "s",
&loc_name, &loc_name_len ) == FAILURE)
{
intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
"locale_parse: unable to parse input params", 0 );
RETURN_FALSE;
}
if(loc_name_len == 0) {
loc_name = intl_locale_get_default();
loc_name_len = strlen(loc_name);
}
INTL_CHECK_LOCALE_LEN(loc_name_len);
array_init( return_value );
/* If the locale is grandfathered, stop, no variants */
if( findOffset( LOC_GRANDFATHERED , loc_name ) >= 0 ){
/* ("Grandfathered Tag. No variants."); */
}
else {
/* Call ICU variant */
variant = get_icu_value_internal( loc_name , LOC_VARIANT_TAG , &result ,0);
if( result > 0 && variant){
/* Tokenize on the "_" or "-" */
token = php_strtok_r( variant->val , DELIMITER , &saved_ptr);
add_next_index_stringl( return_value, token , strlen(token));
/* tokenize on the "_" or "-" and stop at singleton if any */
while( (token = php_strtok_r(NULL , DELIMITER, &saved_ptr)) && (strlen(token)>1) ){
add_next_index_stringl( return_value, token , strlen(token));
}
}
if( variant ){
zend_string_release_ex( variant, 0 );
}
}
}
/* }}} */
/*{{{
* Converts to lower case and also replaces all hyphens with the underscore
*/
static int strToMatch(const char* str ,char *retstr)
{
char* anchor = NULL;
const char* anchor1 = NULL;
int result = 0;
if( (!str) || str[0] == '\0'){
return result;
} else {
anchor = retstr;
anchor1 = str;
while( (*str)!='\0' ){
if( *str == '-' ){
*retstr = '_';
} else {
*retstr = tolower(*str);
}
str++;
retstr++;
}
*retstr = '\0';
retstr= anchor;
str= anchor1;
result = 1;
}
return(result);
}
/* }}} */
/* {{{ proto static boolean Locale::filterMatches(string $langtag, string $locale[, bool $canonicalize])
* Checks if a $langtag filter matches with $locale according to RFC 4647's basic filtering algorithm
*/
/* }}} */
/* {{{ proto bool locale_filter_matches(string $langtag, string $locale[, bool $canonicalize])
* Checks if a $langtag filter matches with $locale according to RFC 4647's basic filtering algorithm
*/
PHP_FUNCTION(locale_filter_matches)
{
char* lang_tag = NULL;
size_t lang_tag_len = 0;
const char* loc_range = NULL;
size_t loc_range_len = 0;
int result = 0;
char* token = 0;
char* chrcheck = NULL;
zend_string* can_lang_tag = NULL;
zend_string* can_loc_range = NULL;
char* cur_lang_tag = NULL;
char* cur_loc_range = NULL;
zend_bool boolCanonical = 0;
UErrorCode status = U_ZERO_ERROR;
intl_error_reset( NULL );
if(zend_parse_parameters( ZEND_NUM_ARGS(), "ss|b",
&lang_tag, &lang_tag_len , &loc_range , &loc_range_len ,
&boolCanonical) == FAILURE)
{
intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
"locale_filter_matches: unable to parse input params", 0 );
RETURN_FALSE;
}
if(loc_range_len == 0) {
loc_range = intl_locale_get_default();
loc_range_len = strlen(loc_range);
}
if( strcmp(loc_range,"*")==0){
RETURN_TRUE;
}
INTL_CHECK_LOCALE_LEN(loc_range_len);
INTL_CHECK_LOCALE_LEN(lang_tag_len);
if( boolCanonical ){
/* canonicalize loc_range */
can_loc_range=get_icu_value_internal( loc_range , LOC_CANONICALIZE_TAG , &result , 0);
if( result ==0) {
intl_error_set( NULL, status,
"locale_filter_matches : unable to canonicalize loc_range" , 0 );
RETURN_FALSE;
}
/* canonicalize lang_tag */
can_lang_tag = get_icu_value_internal( lang_tag , LOC_CANONICALIZE_TAG , &result , 0);
if( result ==0) {
intl_error_set( NULL, status,
"locale_filter_matches : unable to canonicalize lang_tag" , 0 );
RETURN_FALSE;
}
/* Convert to lower case for case-insensitive comparison */
cur_lang_tag = ecalloc( 1, can_lang_tag->len + 1);
/* Convert to lower case for case-insensitive comparison */
result = strToMatch( can_lang_tag->val , cur_lang_tag);
if( result == 0) {
efree( cur_lang_tag );
zend_string_release_ex( can_lang_tag, 0 );
RETURN_FALSE;
}
cur_loc_range = ecalloc( 1, can_loc_range->len + 1);
result = strToMatch( can_loc_range->val , cur_loc_range );
if( result == 0) {
efree( cur_lang_tag );
zend_string_release_ex( can_lang_tag, 0 );
efree( cur_loc_range );
zend_string_release_ex( can_loc_range, 0 );
RETURN_FALSE;
}
/* check if prefix */
token = strstr( cur_lang_tag , cur_loc_range );
if( token && (token==cur_lang_tag) ){
/* check if the char. after match is SEPARATOR */
chrcheck = token + (strlen(cur_loc_range));
if( isIDSeparator(*chrcheck) || isEndOfTag(*chrcheck) ){
efree( cur_lang_tag );
efree( cur_loc_range );
if( can_lang_tag){
zend_string_release_ex( can_lang_tag, 0 );
}
if( can_loc_range){
zend_string_release_ex( can_loc_range, 0 );
}
RETURN_TRUE;
}
}
/* No prefix as loc_range */
if( cur_lang_tag){
efree( cur_lang_tag );
}
if( cur_loc_range){
efree( cur_loc_range );
}
if( can_lang_tag){
zend_string_release_ex( can_lang_tag, 0 );
}
if( can_loc_range){
zend_string_release_ex( can_loc_range, 0 );
}
RETURN_FALSE;
} /* end of if isCanonical */
else{
/* Convert to lower case for case-insensitive comparison */
cur_lang_tag = ecalloc( 1, strlen(lang_tag ) + 1);
result = strToMatch( lang_tag , cur_lang_tag);
if( result == 0) {
efree( cur_lang_tag );
RETURN_FALSE;
}
cur_loc_range = ecalloc( 1, strlen(loc_range ) + 1);
result = strToMatch( loc_range , cur_loc_range );
if( result == 0) {
efree( cur_lang_tag );
efree( cur_loc_range );
RETURN_FALSE;
}
/* check if prefix */
token = strstr( cur_lang_tag , cur_loc_range );
if( token && (token==cur_lang_tag) ){
/* check if the char. after match is SEPARATOR */
chrcheck = token + (strlen(cur_loc_range));
if( isIDSeparator(*chrcheck) || isEndOfTag(*chrcheck) ){
efree( cur_lang_tag );
efree( cur_loc_range );
RETURN_TRUE;
}
}
/* No prefix as loc_range */
if( cur_lang_tag){
efree( cur_lang_tag );
}
if( cur_loc_range){
efree( cur_loc_range );
}
RETURN_FALSE;
}
}
/* }}} */
static void array_cleanup( char* arr[] , int arr_size)
{
int i=0;
for( i=0; i< arr_size; i++ ){
if( arr[i*2] ){
efree( arr[i*2]);
}
}
efree(arr);
}
#define LOOKUP_CLEAN_RETURN(value) array_cleanup(cur_arr, cur_arr_len); return (value)
/* {{{
* returns the lookup result to lookup_loc_range_src_php
* internal function
*/
static zend_string* lookup_loc_range(const char* loc_range, HashTable* hash_arr, int canonicalize )
{
int i = 0;
int cur_arr_len = 0;
int result = 0;
zend_string* lang_tag = NULL;
zval* ele_value = NULL;
char* cur_loc_range = NULL;
zend_string* can_loc_range = NULL;
zend_off_t saved_pos = 0;
zend_string* return_value = NULL;
char **cur_arr = ecalloc(zend_hash_num_elements(hash_arr)*2, sizeof(char *));
ZEND_HASH_FOREACH_VAL(hash_arr, ele_value) {
/* convert the array to lowercase , also replace hyphens with the underscore and store it in cur_arr */
if(Z_TYPE_P(ele_value)!= IS_STRING) {
/* element value is not a string */
intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: locale array element is not a string", 0);
LOOKUP_CLEAN_RETURN(NULL);
}
cur_arr[cur_arr_len*2] = estrndup(Z_STRVAL_P(ele_value), Z_STRLEN_P(ele_value));
result = strToMatch(Z_STRVAL_P(ele_value), cur_arr[cur_arr_len*2]);
if(result == 0) {
intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: unable to canonicalize lang_tag", 0);
LOOKUP_CLEAN_RETURN(NULL);
}
cur_arr[cur_arr_len*2+1] = Z_STRVAL_P(ele_value);
cur_arr_len++ ;
} ZEND_HASH_FOREACH_END(); /* end of for */
/* Canonicalize array elements */
if(canonicalize) {
for(i=0; i<cur_arr_len; i++) {
lang_tag = get_icu_value_internal(cur_arr[i*2], LOC_CANONICALIZE_TAG, &result, 0);
if(result != 1 || lang_tag == NULL || !lang_tag->val[0]) {
if(lang_tag) {
zend_string_release_ex(lang_tag, 0);
}
intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: unable to canonicalize lang_tag" , 0);
LOOKUP_CLEAN_RETURN(NULL);
}
cur_arr[i*2] = erealloc(cur_arr[i*2], lang_tag->len+1);
result = strToMatch(lang_tag->val, cur_arr[i*2]);
zend_string_release_ex(lang_tag, 0);
if(result == 0) {
intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: unable to canonicalize lang_tag" , 0);
LOOKUP_CLEAN_RETURN(NULL);
}
}
}
if(canonicalize) {
/* Canonicalize the loc_range */
can_loc_range = get_icu_value_internal(loc_range, LOC_CANONICALIZE_TAG, &result , 0);
if( result != 1 || can_loc_range == NULL || !can_loc_range->val[0]) {
/* Error */
intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: unable to canonicalize loc_range" , 0 );
if(can_loc_range) {
zend_string_release_ex(can_loc_range, 0);
}
LOOKUP_CLEAN_RETURN(NULL);
} else {
loc_range = can_loc_range->val;
}
}
cur_loc_range = ecalloc(1, strlen(loc_range)+1);
/* convert to lower and replace hyphens */
result = strToMatch(loc_range, cur_loc_range);
if(can_loc_range) {
zend_string_release_ex(can_loc_range, 0);
}
if(result == 0) {
intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: unable to canonicalize lang_tag" , 0);
LOOKUP_CLEAN_RETURN(NULL);
}
/* Lookup for the lang_tag match */
saved_pos = strlen(cur_loc_range);
while(saved_pos > 0) {
for(i=0; i< cur_arr_len; i++){
if(cur_arr[i*2] != NULL && strlen(cur_arr[i*2]) == saved_pos && strncmp(cur_loc_range, cur_arr[i*2], saved_pos) == 0) {
/* Match found */
char *str = canonicalize ? cur_arr[i*2] : cur_arr[i*2+1];
return_value = zend_string_init(str, strlen(str), 0);
efree(cur_loc_range);
LOOKUP_CLEAN_RETURN(return_value);
}
}
saved_pos = getStrrtokenPos(cur_loc_range, saved_pos);
}
/* Match not found */
efree(cur_loc_range);
LOOKUP_CLEAN_RETURN(NULL);
}
/* }}} */
/* {{{ proto string Locale::lookup(array $langtag, string $locale[, bool $canonicalize[, string $default = null]])
* Searches the items in $langtag for the best match to the language
* range
*/
/* }}} */
/* {{{ proto string locale_lookup(array $langtag, string $locale[, bool $canonicalize[, string $default = null]])
* Searches the items in $langtag for the best match to the language
* range
*/
PHP_FUNCTION(locale_lookup)
{
zend_string* fallback_loc_str = NULL;
const char* loc_range = NULL;
size_t loc_range_len = 0;
zval* arr = NULL;
HashTable* hash_arr = NULL;
zend_bool boolCanonical = 0;
zend_string* result_str = NULL;
intl_error_reset( NULL );
if(zend_parse_parameters( ZEND_NUM_ARGS(), "as|bS", &arr, &loc_range, &loc_range_len,
&boolCanonical, &fallback_loc_str) == FAILURE) {
intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "locale_lookup: unable to parse input params", 0 );
RETURN_FALSE;
}
if(loc_range_len == 0) {
if(fallback_loc_str) {
loc_range = ZSTR_VAL(fallback_loc_str);
loc_range_len = ZSTR_LEN(fallback_loc_str);
} else {
loc_range = intl_locale_get_default();
loc_range_len = strlen(loc_range);
}
}
hash_arr = Z_ARRVAL_P(arr);
INTL_CHECK_LOCALE_LEN(loc_range_len);
if( !hash_arr || zend_hash_num_elements( hash_arr ) == 0 ) {
RETURN_EMPTY_STRING();
}
result_str = lookup_loc_range(loc_range, hash_arr, boolCanonical);
if(result_str == NULL || ZSTR_VAL(result_str)[0] == '\0') {
if( fallback_loc_str ) {
result_str = zend_string_copy(fallback_loc_str);
} else {
RETURN_EMPTY_STRING();
}
}
RETURN_STR(result_str);
}
/* }}} */
/* {{{ proto string Locale::acceptFromHttp(string $http_accept)
* Tries to find out best available locale based on HTTP <20>Accept-Language<67> header
*/
/* }}} */
/* {{{ proto string locale_accept_from_http(string $http_accept)
* Tries to find out best available locale based on HTTP <20>Accept-Language<67> header
*/
PHP_FUNCTION(locale_accept_from_http)
{
UEnumeration *available;
char *http_accept = NULL;
size_t http_accept_len;
UErrorCode status = 0;
int len;
char resultLocale[INTL_MAX_LOCALE_LEN+1];
UAcceptResult outResult;
if(zend_parse_parameters( ZEND_NUM_ARGS(), "s", &http_accept, &http_accept_len) == FAILURE)
{
intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
"locale_accept_from_http: unable to parse input parameters", 0 );
RETURN_FALSE;
}
if(http_accept_len > ULOC_FULLNAME_CAPACITY) {
/* check each fragment, if any bigger than capacity, can't do it due to bug #72533 */
char *start = http_accept;
char *end;
size_t len;
do {
end = strchr(start, ',');
len = end ? end-start : http_accept_len-(start-http_accept);
if(len > ULOC_FULLNAME_CAPACITY) {
intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
"locale_accept_from_http: locale string too long", 0 );
RETURN_FALSE;
}
if(end) {
start = end+1;
}
} while(end != NULL);
}
available = ures_openAvailableLocales(NULL, &status);
INTL_CHECK_STATUS(status, "locale_accept_from_http: failed to retrieve locale list");
len = uloc_acceptLanguageFromHTTP(resultLocale, INTL_MAX_LOCALE_LEN,
&outResult, http_accept, available, &status);
uenum_close(available);
INTL_CHECK_STATUS(status, "locale_accept_from_http: failed to find acceptable locale");
if (len < 0 || outResult == ULOC_ACCEPT_FAILED) {
RETURN_FALSE;
}
RETURN_STRINGL(resultLocale, len);
}
/* }}} */