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