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