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