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: Sara Golemon <pollita@php.net>                              |
14   +----------------------------------------------------------------------+
15 */
16
17#include "converter.h"
18#include "zend_exceptions.h"
19
20#include <unicode/utypes.h>
21#include <unicode/ucnv.h>
22#include <unicode/ustring.h>
23
24#include "../intl_error.h"
25
26typedef struct _php_converter_object {
27    zend_object obj;
28#ifdef ZTS
29    void ***tsrm_ls;
30#endif
31    UConverter *src, *dest;
32    zend_fcall_info to_cb, from_cb;
33    zend_fcall_info_cache to_cache, from_cache;
34    intl_error error;
35} php_converter_object;
36
37static zend_class_entry     *php_converter_ce;
38static zend_object_handlers  php_converter_object_handlers;
39
40#define CONV_GET(pzv)  ((php_converter_object*)zend_objects_get_address((pzv) TSRMLS_CC))
41#define THROW_UFAILURE(obj, fname, error) php_converter_throw_failure(obj, error TSRMLS_CC, \
42                                          fname "() returned error %ld: %s", (long)error, u_errorName(error))
43
44/* {{{ php_converter_throw_failure */
45static inline void php_converter_throw_failure(php_converter_object *objval, UErrorCode error TSRMLS_DC, const char *format, ...) {
46    intl_error *err = objval ? &(objval->error) : NULL;
47    char message[1024];
48    va_list vargs;
49
50    va_start(vargs, format);
51    vsnprintf(message, sizeof(message), format, vargs);
52    va_end(vargs);
53
54    intl_errors_set(err, error, message, 1 TSRMLS_CC);
55}
56/* }}} */
57
58/* {{{ php_converter_default_callback */
59static void php_converter_default_callback(zval *return_value, zval *zobj, long reason, zval *error TSRMLS_DC) {
60    zval_dtor(error);
61    ZVAL_LONG(error, U_ZERO_ERROR);
62    /* Basic functionality so children can call parent::toUCallback() */
63    switch (reason) {
64        case UCNV_UNASSIGNED:
65        case UCNV_ILLEGAL:
66        case UCNV_IRREGULAR:
67        {
68            php_converter_object *objval = (php_converter_object*)CONV_GET(zobj);
69            char chars[127];
70            int8_t chars_len = sizeof(chars);
71            UErrorCode uerror = U_ZERO_ERROR;
72            if(!objval->src) {
73                php_converter_throw_failure(objval, U_INVALID_STATE_ERROR TSRMLS_CC, "Source Converter has not been initialized yet");
74                chars[0] = 0x1A;
75                chars[1] = 0;
76                chars_len = 1;
77                ZVAL_LONG(error, U_INVALID_STATE_ERROR);
78                RETVAL_STRINGL(chars, chars_len, 1);
79                return;
80            }
81
82            /* Yes, this is fairly wasteful at first glance,
83             * but considering that the alternative is to store
84             * what's sent into setSubstChars() and the fact
85             * that this is an extremely unlikely codepath
86             * I'd rather take the CPU hit here, than waste time
87             * storing a value I'm unlikely to use.
88             */
89            ucnv_getSubstChars(objval->src, chars, &chars_len, &uerror);
90            if (U_FAILURE(uerror)) {
91                THROW_UFAILURE(objval, "ucnv_getSubstChars", uerror);
92                chars[0] = 0x1A;
93                chars[1] = 0;
94                chars_len = 1;
95                ZVAL_LONG(error, uerror);
96            }
97            RETVAL_STRINGL(chars, chars_len, 1);
98        }
99    }
100}
101/* }}} */
102
103/* {{{ proto void UConverter::toUCallback(long $reason,
104                                          string $source, string $codeUnits,
105                                          long &$error) */
106ZEND_BEGIN_ARG_INFO_EX(php_converter_toUCallback_arginfo, 0, ZEND_RETURN_VALUE, 4)
107    ZEND_ARG_INFO(0, reason)
108    ZEND_ARG_INFO(0, source)
109    ZEND_ARG_INFO(0, codeUnits)
110    ZEND_ARG_INFO(1, error)
111ZEND_END_ARG_INFO();
112static PHP_METHOD(UConverter, toUCallback) {
113    long reason;
114    zval *source, *codeUnits, *error;
115
116    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lzzz",
117        &reason, &source, &codeUnits, &error) == FAILURE) {
118        return;
119    }
120
121    php_converter_default_callback(return_value, getThis(), reason, error TSRMLS_CC);
122}
123/* }}} */
124
125/* {{{ proto void UConverter::fromUCallback(long $reason,
126                                            Array $source, long $codePoint,
127                                            long &$error) */
128ZEND_BEGIN_ARG_INFO_EX(php_converter_fromUCallback_arginfo, 0, ZEND_RETURN_VALUE, 4)
129    ZEND_ARG_INFO(0, reason)
130    ZEND_ARG_INFO(0, source)
131    ZEND_ARG_INFO(0, codePoint)
132    ZEND_ARG_INFO(1, error)
133ZEND_END_ARG_INFO();
134static PHP_METHOD(UConverter, fromUCallback) {
135    long reason;
136    zval *source, *codePoint, *error;
137
138    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lzzz",
139        &reason, &source, &codePoint, &error) == FAILURE) {
140        return;
141    }
142
143    php_converter_default_callback(return_value, getThis(), reason, error TSRMLS_CC);
144}
145/* }}} */
146
147/* {{{ php_converter_check_limits */
148static inline zend_bool php_converter_check_limits(php_converter_object *objval, long available, long needed TSRMLS_DC) {
149    if (available < needed) {
150        php_converter_throw_failure(objval, U_BUFFER_OVERFLOW_ERROR TSRMLS_CC, "Buffer overrun %ld bytes needed, %ld available", needed, available);
151        return 0;
152    }
153    return 1;
154}
155/* }}} */
156
157#define TARGET_CHECK(cnvargs, needed) php_converter_check_limits(objval, cnvargs->targetLimit - cnvargs->target, needed TSRMLS_CC)
158
159/* {{{ php_converter_append_toUnicode_target */
160static void php_converter_append_toUnicode_target(zval *val, UConverterToUnicodeArgs *args, php_converter_object *objval TSRMLS_DC) {
161    switch (Z_TYPE_P(val)) {
162        case IS_NULL:
163            /* Code unit is being skipped */
164            return;
165        case IS_LONG:
166        {
167            long lval = Z_LVAL_P(val);
168            if ((lval < 0) || (lval > 0x10FFFF)) {
169                php_converter_throw_failure(objval, U_ILLEGAL_ARGUMENT_ERROR TSRMLS_CC, "Invalid codepoint U+%04lx", lval);
170                return;
171            }
172            if (lval > 0xFFFF) {
173                /* Supplemental planes U+010000 - U+10FFFF */
174                if (TARGET_CHECK(args, 2)) {
175                    /* TODO: Find the ICU call which does this properly */
176                    *(args->target++) = (UChar)(((lval - 0x10000) >> 10)   | 0xD800);
177                    *(args->target++) = (UChar)(((lval - 0x10000) & 0x3FF) | 0xDC00);
178                }
179                return;
180            }
181            /* Non-suggogate BMP codepoint */
182            if (TARGET_CHECK(args, 1)) {
183                *(args->target++) = (UChar)lval;
184            }
185            return;
186        }
187        case IS_STRING:
188        {
189            const char *strval = Z_STRVAL_P(val);
190            int i = 0, strlen = Z_STRLEN_P(val);
191
192            while((i != strlen) && TARGET_CHECK(args, 1)) {
193                UChar c;
194                U8_NEXT(strval, i, strlen, c);
195                *(args->target++) = c;
196            }
197            return;
198        }
199        case IS_ARRAY:
200        {
201            HashTable *ht = Z_ARRVAL_P(val);
202            HashPosition pos;
203            zval **tmpzval;
204
205            for(zend_hash_internal_pointer_reset_ex(ht, &pos);
206                zend_hash_get_current_data_ex(ht, (void**)&tmpzval, &pos) == SUCCESS;
207                zend_hash_move_forward_ex(ht, &pos)) {
208                php_converter_append_toUnicode_target(*tmpzval, args, objval TSRMLS_CC);
209            }
210            return;
211        }
212        default:
213            php_converter_throw_failure(objval, U_ILLEGAL_ARGUMENT_ERROR TSRMLS_CC,
214                                                    "toUCallback() specified illegal type for substitution character");
215    }
216}
217/* }}} */
218
219/* {{{ php_converter_to_u_callback */
220static void php_converter_to_u_callback(const void *context,
221                                        UConverterToUnicodeArgs *args,
222                                        const char *codeUnits, int32_t length,
223                                        UConverterCallbackReason reason,
224                                        UErrorCode *pErrorCode) {
225    php_converter_object *objval = (php_converter_object*)context;
226    zval *zreason, *zsource, *zcodeunits, *zerror, *retval = NULL;
227    zval **zargs[4];
228#ifdef ZTS
229    TSRMLS_D = objval->tsrm_ls;
230#endif
231
232    MAKE_STD_ZVAL(zreason);
233    ZVAL_LONG(zreason, reason);
234    zargs[0] = &zreason;
235
236    MAKE_STD_ZVAL(zsource);
237    ZVAL_STRINGL(zsource, args->source, args->sourceLimit - args->source, 1);
238    zargs[1] = &zsource;
239
240    MAKE_STD_ZVAL(zcodeunits);
241    ZVAL_STRINGL(zcodeunits, codeUnits, length, 1);
242    zargs[2] = &zcodeunits;
243
244    MAKE_STD_ZVAL(zerror);
245    ZVAL_LONG(zerror, *pErrorCode);
246    zargs[3] = &zerror;
247
248    objval->to_cb.param_count    = 4;
249    objval->to_cb.params         = zargs;
250    objval->to_cb.retval_ptr_ptr = &retval;
251    objval->to_cb.no_separation  = 0;
252    if (zend_call_function(&(objval->to_cb), &(objval->to_cache) TSRMLS_CC) == FAILURE) {
253        /* Unlikely */
254        php_converter_throw_failure(objval, U_INTERNAL_PROGRAM_ERROR TSRMLS_CC, "Unexpected failure calling toUCallback()");
255    } else if (retval) {
256        php_converter_append_toUnicode_target(retval, args, objval TSRMLS_CC);
257        zval_ptr_dtor(&retval);
258    }
259
260    if (Z_TYPE_P(zerror) == IS_LONG) {
261        *pErrorCode = Z_LVAL_P(zerror);
262    }
263
264    zval_ptr_dtor(&zreason);
265    zval_ptr_dtor(&zsource);
266    zval_ptr_dtor(&zcodeunits);
267    zval_ptr_dtor(&zerror);
268}
269/* }}} */
270
271/* {{{ php_converter_append_fromUnicode_target */
272static void php_converter_append_fromUnicode_target(zval *val, UConverterFromUnicodeArgs *args, php_converter_object *objval TSRMLS_DC) {
273    switch (Z_TYPE_P(val)) {
274        case IS_NULL:
275            /* Ignore */
276            return;
277        case IS_LONG:
278            if (TARGET_CHECK(args, 1)) {
279                *(args->target++) = Z_LVAL_P(val);
280            }
281            return;
282        case IS_STRING:
283        {
284            int vallen = Z_STRLEN_P(val);
285            if (TARGET_CHECK(args, vallen)) {
286                memcpy(args->target, Z_STRVAL_P(val), vallen);
287                args->target += vallen;
288            }
289            return;
290        }
291        case IS_ARRAY:
292        {
293            HashTable *ht = Z_ARRVAL_P(val);
294            HashPosition pos;
295            zval **tmpzval;
296            for(zend_hash_internal_pointer_reset_ex(ht, &pos);
297                zend_hash_get_current_data_ex(ht, (void**)&tmpzval, &pos) == SUCCESS;
298                zend_hash_move_forward_ex(ht, &pos)) {
299                php_converter_append_fromUnicode_target(*tmpzval, args, objval TSRMLS_CC);
300            }
301            return;
302        }
303        default:
304            php_converter_throw_failure(objval, U_ILLEGAL_ARGUMENT_ERROR TSRMLS_CC, "fromUCallback() specified illegal type for substitution character");
305    }
306}
307/* }}} */
308
309/* {{{ php_converter_from_u_callback */
310static void php_converter_from_u_callback(const void *context,
311                                          UConverterFromUnicodeArgs *args,
312                                          const UChar *codeUnits, int32_t length, UChar32 codePoint,
313                                          UConverterCallbackReason reason,
314                                          UErrorCode *pErrorCode) {
315    php_converter_object *objval = (php_converter_object*)context;
316    zval *zreason, *zsource, *zcodepoint, *zerror, *retval = NULL;
317    zval **zargs[4];
318    int i;
319#ifdef ZTS
320    TSRMLS_D = objval->tsrm_ls;
321#endif
322
323    MAKE_STD_ZVAL(zreason);
324    ZVAL_LONG(zreason, reason);
325    zargs[0] = &zreason;
326
327    MAKE_STD_ZVAL(zsource);
328    array_init(zsource);
329    i = 0;
330    while (i < length) {
331        UChar32 c;
332        U16_NEXT(codeUnits, i, length, c);
333        add_next_index_long(zsource, c);
334    }
335    zargs[1] = &zsource;
336
337    MAKE_STD_ZVAL(zcodepoint);
338    ZVAL_LONG(zcodepoint, codePoint);
339    zargs[2] = &zcodepoint;
340
341    MAKE_STD_ZVAL(zerror);
342    ZVAL_LONG(zerror, *pErrorCode);
343    zargs[3] = &zerror;
344
345    objval->from_cb.param_count    = 4;
346    objval->from_cb.params         = zargs;
347    objval->from_cb.retval_ptr_ptr = &retval;
348    objval->from_cb.no_separation  = 0;
349    if (zend_call_function(&(objval->from_cb), &(objval->from_cache) TSRMLS_CC) == FAILURE) {
350        /* Unlikely */
351        php_converter_throw_failure(objval, U_INTERNAL_PROGRAM_ERROR TSRMLS_CC, "Unexpected failure calling fromUCallback()");
352    } else if (retval) {
353        php_converter_append_fromUnicode_target(retval, args, objval TSRMLS_CC);
354        zval_ptr_dtor(&retval);
355    }
356
357    if (Z_TYPE_P(zerror) == IS_LONG) {
358        *pErrorCode = Z_LVAL_P(zerror);
359    }
360
361    zval_ptr_dtor(&zreason);
362    zval_ptr_dtor(&zsource);
363    zval_ptr_dtor(&zcodepoint);
364    zval_ptr_dtor(&zerror);
365}
366/* }}} */
367
368/* {{{ php_converter_set_callbacks */
369static inline zend_bool php_converter_set_callbacks(php_converter_object *objval, UConverter *cnv TSRMLS_DC) {
370    zend_bool ret = 1;
371    UErrorCode error = U_ZERO_ERROR;
372
373    if (objval->obj.ce == php_converter_ce) {
374        /* Short-circuit having to go through method calls and data marshalling
375         * when we're using default behavior
376         */
377        return 1;
378    }
379
380    ucnv_setToUCallBack(cnv, (UConverterToUCallback)php_converter_to_u_callback, (const void*)objval,
381                                 NULL, NULL, &error);
382    if (U_FAILURE(error)) {
383        THROW_UFAILURE(objval, "ucnv_setToUCallBack", error);
384        ret = 0;
385    }
386
387    error = U_ZERO_ERROR;
388    ucnv_setFromUCallBack(cnv, (UConverterFromUCallback)php_converter_from_u_callback, (const void*)objval,
389                                    NULL, NULL, &error);
390    if (U_FAILURE(error)) {
391        THROW_UFAILURE(objval, "ucnv_setFromUCallBack", error);
392        ret = 0;
393    }
394    return ret;
395}
396/* }}} */
397
398/* {{{ php_converter_set_encoding */
399static zend_bool php_converter_set_encoding(php_converter_object *objval,
400                                            UConverter **pcnv,
401                                            const char *enc, int enc_len
402                                            TSRMLS_DC) {
403    UErrorCode error = U_ZERO_ERROR;
404    UConverter *cnv = ucnv_open(enc, &error);
405
406    if (error == U_AMBIGUOUS_ALIAS_WARNING) {
407        UErrorCode getname_error = U_ZERO_ERROR;
408        const char *actual_encoding = ucnv_getName(cnv, &getname_error);
409        if (U_FAILURE(getname_error)) {
410            /* Should never happen */
411            actual_encoding = "(unknown)";
412        }
413        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Ambiguous encoding specified, using %s", actual_encoding);
414    } else if (U_FAILURE(error)) {
415        if (objval) {
416            THROW_UFAILURE(objval, "ucnv_open", error);
417        } else {
418            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error setting encoding: %d - %s", (int)error, u_errorName(error));
419        }
420        return 0;
421    }
422
423    if (objval && !php_converter_set_callbacks(objval, cnv TSRMLS_CC)) {
424        return 0;
425    }
426
427    if (*pcnv) {
428        ucnv_close(*pcnv);
429    }
430    *pcnv = cnv;
431    return 1;
432}
433/* }}} */
434
435/* {{{ php_converter_do_set_encoding */
436ZEND_BEGIN_ARG_INFO_EX(php_converter_set_encoding_arginfo, 0, ZEND_RETURN_VALUE, 1)
437    ZEND_ARG_INFO(0, encoding)
438ZEND_END_ARG_INFO();
439static void php_converter_do_set_encoding(UConverter *cnv, INTERNAL_FUNCTION_PARAMETERS) {
440    php_converter_object *objval = CONV_GET(getThis());
441    char *enc;
442    int enc_len;
443
444    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &enc, &enc_len) == FAILURE) {
445        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "Bad arguments, "
446                "expected one string argument", 0 TSRMLS_CC);
447        RETURN_FALSE;
448    }
449    intl_errors_reset(&objval->error TSRMLS_CC);
450
451    RETURN_BOOL(php_converter_set_encoding(objval, &(objval->src), enc, enc_len TSRMLS_CC));
452}
453/* }}} */
454
455/* {{{ proto bool UConverter::setSourceEncoding(string encoding) */
456static PHP_METHOD(UConverter, setSourceEncoding) {
457    php_converter_object *objval = CONV_GET(getThis());
458    php_converter_do_set_encoding(objval->src, INTERNAL_FUNCTION_PARAM_PASSTHRU);
459}
460/* }}} */
461
462/* {{{ proto bool UConverter::setDestinationEncoding(string encoding) */
463static PHP_METHOD(UConverter, setDestinationEncoding) {
464    php_converter_object *objval = CONV_GET(getThis());
465    php_converter_do_set_encoding(objval->dest, INTERNAL_FUNCTION_PARAM_PASSTHRU);
466}
467/* }}} */
468
469/* {{{ php_converter_do_get_encoding */
470ZEND_BEGIN_ARG_INFO_EX(php_converter_get_encoding_arginfo, 0, ZEND_RETURN_VALUE, 0)
471ZEND_END_ARG_INFO();
472static void php_converter_do_get_encoding(php_converter_object *objval, UConverter *cnv, INTERNAL_FUNCTION_PARAMETERS) {
473    const char *name;
474
475    if (zend_parse_parameters_none() == FAILURE) {
476        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "Expected no arguments", 0 TSRMLS_CC);
477        RETURN_FALSE;
478    }
479
480    intl_errors_reset(&objval->error TSRMLS_CC);
481
482    if (!cnv) {
483        RETURN_NULL();
484    }
485
486    name = ucnv_getName(cnv, &objval->error.code);
487    if (U_FAILURE(objval->error.code)) {
488        THROW_UFAILURE(objval, "ucnv_getName()", objval->error.code);
489        RETURN_FALSE;
490    }
491
492    RETURN_STRING(name, 1);
493}
494/* }}} */
495
496/* {{{ proto string UConverter::getSourceEncoding() */
497static PHP_METHOD(UConverter, getSourceEncoding) {
498    php_converter_object *objval = CONV_GET(getThis());
499    php_converter_do_get_encoding(objval, objval->src, INTERNAL_FUNCTION_PARAM_PASSTHRU);
500}
501/* }}} */
502
503/* {{{ proto string UConverter::getDestinationEncoding() */
504static PHP_METHOD(UConverter, getDestinationEncoding) {
505        php_converter_object *objval = CONV_GET(getThis());
506        php_converter_do_get_encoding(objval, objval->dest, INTERNAL_FUNCTION_PARAM_PASSTHRU);
507}
508/* }}} */
509
510/* {{{ php_converter_do_get_type */
511ZEND_BEGIN_ARG_INFO_EX(php_converter_get_type_arginfo, 0, ZEND_RETURN_VALUE, 0)
512ZEND_END_ARG_INFO();
513static void php_converter_do_get_type(php_converter_object *objval, UConverter *cnv, INTERNAL_FUNCTION_PARAMETERS) {
514    UConverterType t;
515
516    if (zend_parse_parameters_none() == FAILURE) {
517        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "Expected no arguments", 0 TSRMLS_CC);
518        RETURN_FALSE;
519    }
520    intl_errors_reset(&objval->error TSRMLS_CC);
521
522    if (!cnv) {
523        RETURN_NULL();
524    }
525
526    t = ucnv_getType(cnv);
527    if (U_FAILURE(objval->error.code)) {
528        THROW_UFAILURE(objval, "ucnv_getType", objval->error.code);
529        RETURN_FALSE;
530    }
531
532    RETURN_LONG(t);
533}
534/* }}} */
535
536/* {{{ proto long UConverter::getSourceType() */
537static PHP_METHOD(UConverter, getSourceType) {
538    php_converter_object *objval = CONV_GET(getThis());
539    php_converter_do_get_type(objval, objval->src, INTERNAL_FUNCTION_PARAM_PASSTHRU);
540}
541/* }}} */
542
543/* {{{ proto long UConverter::getDestinationType() */
544static PHP_METHOD(UConverter, getDestinationType) {
545    php_converter_object *objval = CONV_GET(getThis());
546    php_converter_do_get_type(objval, objval->dest, INTERNAL_FUNCTION_PARAM_PASSTHRU);
547}
548/* }}} */
549
550/* {{{ php_converter_resolve_callback */
551static void php_converter_resolve_callback(zval *zobj,
552                                           php_converter_object *objval,
553                                           const char *callback_name,
554                                           zend_fcall_info *finfo,
555                                           zend_fcall_info_cache *fcache TSRMLS_DC) {
556    char *errstr = NULL;
557    zval caller;
558
559    array_init(&caller);
560    Z_ADDREF_P(zobj);
561    add_index_zval(&caller, 0, zobj);
562    add_index_string(&caller, 1, callback_name, 1);
563    if (zend_fcall_info_init(&caller, 0, finfo, fcache, NULL, &errstr TSRMLS_CC) == FAILURE) {
564        php_converter_throw_failure(objval, U_INTERNAL_PROGRAM_ERROR TSRMLS_CC, "Error setting converter callback: %s", errstr);
565    }
566    zval_dtor(&caller);
567    if (errstr) {
568        efree(errstr);
569    }
570}
571/* }}} */
572
573/* {{{ proto void UConverter::__construct([string dest = 'utf-8',[string src = 'utf-8']]) */
574ZEND_BEGIN_ARG_INFO_EX(php_converter_arginfo, 0, ZEND_RETURN_VALUE, 0)
575    ZEND_ARG_INFO(0, destination_encoding)
576    ZEND_ARG_INFO(0, source_encoding)
577ZEND_END_ARG_INFO();
578
579static PHP_METHOD(UConverter, __construct) {
580    php_converter_object *objval = CONV_GET(getThis());
581    char *src = "utf-8";
582    int src_len = sizeof("utf-8") - 1;
583    char *dest = src;
584    int dest_len = src_len;
585
586    intl_error_reset(NULL TSRMLS_CC);
587
588    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!s!",
589                              &dest, &dest_len, &src, &src_len) == FAILURE) {
590        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
591            "UConverter::__construct(): bad arguments", 0 TSRMLS_CC);
592        return;
593    }
594
595    php_converter_set_encoding(objval, &(objval->src),  src,  src_len  TSRMLS_CC);
596    php_converter_set_encoding(objval, &(objval->dest), dest, dest_len TSRMLS_CC);
597    php_converter_resolve_callback(getThis(), objval, "toUCallback",   &(objval->to_cb),   &(objval->to_cache) TSRMLS_CC);
598    php_converter_resolve_callback(getThis(), objval, "fromUCallback", &(objval->from_cb), &(objval->from_cache) TSRMLS_CC);
599}
600/* }}} */
601
602/* {{{ proto bool UConverter::setSubstChars(string $chars) */
603ZEND_BEGIN_ARG_INFO_EX(php_converter_setSubstChars_arginfo, 0, ZEND_RETURN_VALUE, 1)
604    ZEND_ARG_INFO(0, chars)
605ZEND_END_ARG_INFO();
606
607static PHP_METHOD(UConverter, setSubstChars) {
608    php_converter_object *objval = CONV_GET(getThis());
609    char *chars;
610    int chars_len, ret = 1;
611
612    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &chars, &chars_len) == FAILURE) {
613        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
614            "UConverter::setSubstChars(): bad arguments", 0 TSRMLS_CC);
615        RETURN_FALSE;
616    }
617    intl_errors_reset(&objval->error TSRMLS_CC);
618
619    if (objval->src) {
620        UErrorCode error = U_ZERO_ERROR;
621        ucnv_setSubstChars(objval->src, chars, chars_len, &error);
622        if (U_FAILURE(error)) {
623            THROW_UFAILURE(objval, "ucnv_setSubstChars", error);
624            ret = 0;
625        }
626    } else {
627        php_converter_throw_failure(objval, U_INVALID_STATE_ERROR TSRMLS_CC, "Source Converter has not been initialized yet");
628        ret = 0;
629    }
630
631    if (objval->dest) {
632        UErrorCode error = U_ZERO_ERROR;
633        ucnv_setSubstChars(objval->dest, chars, chars_len, &error);
634        if (U_FAILURE(error)) {
635            THROW_UFAILURE(objval, "ucnv_setSubstChars", error);
636            ret = 0;
637        }
638    } else {
639        php_converter_throw_failure(objval, U_INVALID_STATE_ERROR TSRMLS_CC, "Destination Converter has not been initialized yet");
640        ret = 0;
641    }
642
643    RETURN_BOOL(ret);
644}
645/* }}} */
646
647/* {{{ proto string UConverter::getSubstChars() */
648ZEND_BEGIN_ARG_INFO_EX(php_converter_getSubstChars_arginfo, 0, ZEND_RETURN_VALUE, 0)
649ZEND_END_ARG_INFO();
650
651static PHP_METHOD(UConverter, getSubstChars) {
652    php_converter_object *objval = CONV_GET(getThis());
653    char chars[127];
654    int8_t chars_len = sizeof(chars);
655    UErrorCode error = U_ZERO_ERROR;
656
657    if (zend_parse_parameters_none() == FAILURE) {
658        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
659            "UConverter::getSubstChars(): expected no arguments", 0 TSRMLS_CC);
660        RETURN_FALSE;
661    }
662    intl_errors_reset(&objval->error TSRMLS_CC);
663
664    if (!objval->src) {
665        RETURN_NULL();
666    }
667
668    /* src and dest get the same subst chars set,
669     * so it doesn't really matter which one we read from
670     */
671    ucnv_getSubstChars(objval->src, chars, &chars_len, &error);
672    if (U_FAILURE(error)) {
673        THROW_UFAILURE(objval, "ucnv_getSubstChars", error);
674        RETURN_FALSE;
675    }
676
677    RETURN_STRINGL(chars, chars_len, 1);
678}
679/* }}} */
680
681/* {{{ php_converter_do_convert */
682static zend_bool php_converter_do_convert(UConverter *dest_cnv, char **pdest, int32_t *pdest_len,
683                                          UConverter *src_cnv,  const char *src, int32_t src_len,
684                                          php_converter_object *objval
685                                          TSRMLS_DC) {
686    UErrorCode  error = U_ZERO_ERROR;
687    int32_t     dest_len,
688                temp_len;
689    char        *dest;
690    UChar       *temp;
691
692    if (!src_cnv || !dest_cnv) {
693        php_converter_throw_failure(objval, U_INVALID_STATE_ERROR TSRMLS_CC,
694                                    "Internal converters not initialized");
695        return 0;
696    }
697
698    /* Get necessary buffer size first */
699    temp_len = 1 + ucnv_toUChars(src_cnv, NULL, 0, src, src_len, &error);
700    if (U_FAILURE(error) && error != U_BUFFER_OVERFLOW_ERROR) {
701        THROW_UFAILURE(objval, "ucnv_toUChars", error);
702        return 0;
703    }
704    temp = safe_emalloc(sizeof(UChar), temp_len, sizeof(UChar));
705
706    /* Convert to intermediate UChar* array */
707    error = U_ZERO_ERROR;
708    temp_len = ucnv_toUChars(src_cnv, temp, temp_len, src, src_len, &error);
709    if (U_FAILURE(error)) {
710        THROW_UFAILURE(objval, "ucnv_toUChars", error);
711        efree(temp);
712        return 0;
713    }
714    temp[temp_len] = 0;
715
716    /* Get necessary output buffer size */
717    dest_len = 1 + ucnv_fromUChars(dest_cnv, NULL, 0, temp, temp_len, &error);
718    if (U_FAILURE(error) && error != U_BUFFER_OVERFLOW_ERROR) {
719        THROW_UFAILURE(objval, "ucnv_fromUChars", error);
720        efree(temp);
721        return 0;
722    }
723    dest = safe_emalloc(sizeof(char), dest_len, sizeof(char));
724
725    /* Convert to final encoding */
726    error = U_ZERO_ERROR;
727    dest_len = ucnv_fromUChars(dest_cnv, dest, dest_len, temp, temp_len, &error);
728    efree(temp);
729    if (U_FAILURE(error)) {
730        THROW_UFAILURE(objval, "ucnv_fromUChars", error);
731        efree(dest);
732        return 0;
733    }
734
735    *pdest = dest;
736    if (pdest_len) {
737        *pdest_len = dest_len;
738    }
739
740    return 1;
741}
742/* }}} */
743
744/* {{{ proto string UConverter::reasonText(long reason) */
745#define UCNV_REASON_CASE(v) case (UCNV_ ## v) : RETURN_STRINGL( "REASON_" #v , sizeof( "REASON_" #v ) - 1, 1);
746ZEND_BEGIN_ARG_INFO_EX(php_converter_reasontext_arginfo, 0, ZEND_RETURN_VALUE, 0)
747    ZEND_ARG_INFO(0, reason)
748ZEND_END_ARG_INFO();
749static PHP_METHOD(UConverter, reasonText) {
750    long reason;
751
752    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &reason) == FAILURE) {
753        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
754            "UConverter::reasonText(): bad arguments", 0 TSRMLS_CC);
755        RETURN_FALSE;
756    }
757    intl_error_reset(NULL TSRMLS_CC);
758
759    switch (reason) {
760        UCNV_REASON_CASE(UNASSIGNED)
761        UCNV_REASON_CASE(ILLEGAL)
762        UCNV_REASON_CASE(IRREGULAR)
763        UCNV_REASON_CASE(RESET)
764        UCNV_REASON_CASE(CLOSE)
765        UCNV_REASON_CASE(CLONE)
766        default:
767            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown UConverterCallbackReason: %ld", reason);
768            RETURN_FALSE;
769    }
770}
771/* }}} */
772
773/* {{{ proto string UConverter::convert(string str[, bool reverse]) */
774ZEND_BEGIN_ARG_INFO_EX(php_converter_convert_arginfo, 0, ZEND_RETURN_VALUE, 1)
775        ZEND_ARG_INFO(0, str)
776    ZEND_ARG_INFO(0, reverse)
777ZEND_END_ARG_INFO();
778
779static PHP_METHOD(UConverter, convert) {
780        php_converter_object *objval = CONV_GET(getThis());
781    char *str, *dest;
782    int str_len, dest_len;
783    zend_bool reverse = 0;
784
785    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b",
786                              &str, &str_len, &reverse) == FAILURE) {
787        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
788            "UConverter::convert(): bad arguments", 0 TSRMLS_CC);
789        RETURN_FALSE;
790    }
791    intl_errors_reset(&objval->error TSRMLS_CC);
792
793    if (php_converter_do_convert(reverse ? objval->src : objval->dest,
794                                 &dest, &dest_len,
795                                 reverse ? objval->dest : objval->src,
796                                 str,   str_len,
797                                 objval TSRMLS_CC)) {
798        RETURN_STRINGL(dest, dest_len, 0);
799    } else {
800        RETURN_FALSE;
801    }
802}
803/* }}} */
804
805/* {{{ proto string UConverter::transcode(string $str, string $toEncoding, string $fromEncoding[, Array $options = array()]) */
806ZEND_BEGIN_ARG_INFO_EX(php_converter_transcode_arginfo, 0, ZEND_RETURN_VALUE, 3)
807    ZEND_ARG_INFO(0, str)
808    ZEND_ARG_INFO(0, toEncoding)
809    ZEND_ARG_INFO(0, fromEncoding)
810    ZEND_ARG_ARRAY_INFO(0, options, 1)
811ZEND_END_ARG_INFO();
812
813static PHP_METHOD(UConverter, transcode) {
814    char *str, *src, *dest;
815    int str_len, src_len, dest_len;
816    zval *options = NULL;
817    UConverter *src_cnv = NULL, *dest_cnv = NULL;
818
819    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|a!",
820            &str, &str_len, &dest, &dest_len, &src, &src_len, &options) == FAILURE) {
821        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
822            "UConverter::transcode(): bad arguments", 0 TSRMLS_CC);
823        RETURN_FALSE;
824    }
825    intl_error_reset(NULL TSRMLS_CC);
826
827    if (php_converter_set_encoding(NULL, &src_cnv,  src,  src_len TSRMLS_CC) &&
828        php_converter_set_encoding(NULL, &dest_cnv, dest, dest_len TSRMLS_CC)) {
829        char *out = NULL;
830        int out_len = 0;
831        UErrorCode error = U_ZERO_ERROR;
832
833        if (options && zend_hash_num_elements(Z_ARRVAL_P(options))) {
834            zval **tmpzval;
835
836            if (U_SUCCESS(error) &&
837                zend_hash_find(Z_ARRVAL_P(options), "from_subst", sizeof("from_subst"), (void**)&tmpzval) == SUCCESS &&
838                Z_TYPE_PP(tmpzval) == IS_STRING) {
839                error = U_ZERO_ERROR;
840                ucnv_setSubstChars(src_cnv, Z_STRVAL_PP(tmpzval), Z_STRLEN_PP(tmpzval) & 0x7F, &error);
841            }
842            if (U_SUCCESS(error) &&
843                zend_hash_find(Z_ARRVAL_P(options), "to_subst", sizeof("to_subst"), (void**)&tmpzval) == SUCCESS &&
844                Z_TYPE_PP(tmpzval) == IS_STRING) {
845                error = U_ZERO_ERROR;
846                ucnv_setSubstChars(dest_cnv, Z_STRVAL_PP(tmpzval), Z_STRLEN_PP(tmpzval) & 0x7F, &error);
847            }
848        }
849
850        if (U_SUCCESS(error) &&
851            php_converter_do_convert(dest_cnv, &out, &out_len, src_cnv, str, str_len, NULL TSRMLS_CC)) {
852            RETVAL_STRINGL(out, out_len, 0);
853        }
854
855        if (U_FAILURE(error)) {
856            THROW_UFAILURE(NULL, "transcode", error);
857            RETVAL_FALSE;
858        }
859    } else {
860        RETVAL_FALSE;
861    }
862
863    if (src_cnv) {
864        ucnv_close(src_cnv);
865    }
866    if (dest_cnv) {
867        ucnv_close(dest_cnv);
868    }
869}
870/* }}} */
871
872/* {{{ proto int UConverter::getErrorCode() */
873ZEND_BEGIN_ARG_INFO_EX(php_converter_geterrorcode_arginfo, 0, ZEND_RETURN_VALUE, 0)
874ZEND_END_ARG_INFO();
875static PHP_METHOD(UConverter, getErrorCode) {
876    php_converter_object *objval = CONV_GET(getThis());
877
878    if (zend_parse_parameters_none() == FAILURE) {
879        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
880            "UConverter::getErrorCode(): expected no arguments", 0 TSRMLS_CC);
881        RETURN_FALSE;
882    }
883
884    RETURN_LONG(intl_error_get_code(&(objval->error) TSRMLS_CC));
885}
886/* }}} */
887
888/* {{{ proto string UConverter::getErrorMessage() */
889ZEND_BEGIN_ARG_INFO_EX(php_converter_geterrormsg_arginfo, 0, ZEND_RETURN_VALUE, 0)
890ZEND_END_ARG_INFO();
891static PHP_METHOD(UConverter, getErrorMessage) {
892    php_converter_object *objval = CONV_GET(getThis());
893    char *message = intl_error_get_message(&(objval->error) TSRMLS_CC);
894
895    if (zend_parse_parameters_none() == FAILURE) {
896        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
897            "UConverter::getErrorMessage(): expected no arguments", 0 TSRMLS_CC);
898        RETURN_FALSE;
899    }
900
901    if (message) {
902        RETURN_STRING(message, 1);
903    } else {
904        RETURN_NULL();
905    }
906}
907/* }}} */
908
909/* {{{ proto array UConverter::getAvailable() */
910ZEND_BEGIN_ARG_INFO_EX(php_converter_getavailable_arginfo, 0, ZEND_RETURN_VALUE, 0)
911ZEND_END_ARG_INFO();
912static PHP_METHOD(UConverter, getAvailable) {
913    int32_t i,
914            count = ucnv_countAvailable();
915
916    if (zend_parse_parameters_none() == FAILURE) {
917        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
918            "UConverter::getErrorMessage(): expected no arguments", 0 TSRMLS_CC);
919        RETURN_FALSE;
920    }
921    intl_error_reset(NULL TSRMLS_CC);
922
923    array_init(return_value);
924    for(i = 0; i < count; i++) {
925        const char *name = ucnv_getAvailableName(i);
926        add_next_index_string(return_value, name, 1);
927    }
928}
929/* }}} */
930
931/* {{{ proto array UConverter::getAliases(string name) */
932ZEND_BEGIN_ARG_INFO_EX(php_converter_getaliases_arginfo, 0, ZEND_RETURN_VALUE, 0)
933    ZEND_ARG_INFO(0, name)
934ZEND_END_ARG_INFO();
935static PHP_METHOD(UConverter, getAliases) {
936    char *name;
937    int name_len;
938    UErrorCode error = U_ZERO_ERROR;
939    uint16_t i, count;
940
941    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) {
942        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
943            "UConverter::getAliases(): bad arguments", 0 TSRMLS_CC);
944        RETURN_FALSE;
945    }
946    intl_error_reset(NULL TSRMLS_CC);
947
948    count = ucnv_countAliases(name, &error);
949    if (U_FAILURE(error)) {
950        THROW_UFAILURE(NULL, "ucnv_countAliases", error);
951        RETURN_FALSE;
952    }
953
954    array_init(return_value);
955    for(i = 0; i < count; i++) {
956        const char *alias;
957
958        error = U_ZERO_ERROR;
959        alias = ucnv_getAlias(name, i, &error);
960        if (U_FAILURE(error)) {
961            THROW_UFAILURE(NULL, "ucnv_getAlias", error);
962            zval_dtor(return_value);
963            RETURN_NULL();
964        }
965        add_next_index_string(return_value, alias, 1);
966    }
967}
968/* }}} */
969
970/* {{{ proto array UConverter::getStandards() */
971ZEND_BEGIN_ARG_INFO_EX(php_converter_getstandards_arginfo, 0, ZEND_RETURN_VALUE, 0)
972ZEND_END_ARG_INFO();
973static PHP_METHOD(UConverter, getStandards) {
974    uint16_t i, count;
975
976    if (zend_parse_parameters_none() == FAILURE) {
977        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
978            "UConverter::getStandards(): expected no arguments", 0 TSRMLS_CC);
979        RETURN_FALSE;
980    }
981    intl_error_reset(NULL TSRMLS_CC);
982
983    array_init(return_value);
984    count = ucnv_countStandards();
985    for(i = 0; i < count; i++) {
986        UErrorCode error = U_ZERO_ERROR;
987        const char *name = ucnv_getStandard(i, &error);
988        if (U_FAILURE(error)) {
989            THROW_UFAILURE(NULL, "ucnv_getStandard", error);
990            zval_dtor(return_value);
991            RETURN_NULL();
992        }
993        add_next_index_string(return_value, name, 1);
994    }
995}
996/* }}} */
997
998static zend_function_entry php_converter_methods[] = {
999    PHP_ME(UConverter, __construct,            php_converter_arginfo,                   ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
1000
1001    /* Encoding selection */
1002    PHP_ME(UConverter, setSourceEncoding,      php_converter_set_encoding_arginfo,      ZEND_ACC_PUBLIC)
1003    PHP_ME(UConverter, setDestinationEncoding, php_converter_set_encoding_arginfo,      ZEND_ACC_PUBLIC)
1004    PHP_ME(UConverter, getSourceEncoding,      php_converter_get_encoding_arginfo,      ZEND_ACC_PUBLIC)
1005    PHP_ME(UConverter, getDestinationEncoding, php_converter_get_encoding_arginfo,      ZEND_ACC_PUBLIC)
1006
1007    /* Introspection for algorithmic converters */
1008    PHP_ME(UConverter, getSourceType,          php_converter_get_type_arginfo,          ZEND_ACC_PUBLIC)
1009    PHP_ME(UConverter, getDestinationType,     php_converter_get_type_arginfo,          ZEND_ACC_PUBLIC)
1010
1011    /* Basic codeunit error handling */
1012    PHP_ME(UConverter, getSubstChars,          php_converter_getSubstChars_arginfo,     ZEND_ACC_PUBLIC)
1013    PHP_ME(UConverter, setSubstChars,          php_converter_setSubstChars_arginfo,     ZEND_ACC_PUBLIC)
1014
1015    /* Default callback handlers */
1016    PHP_ME(UConverter, toUCallback,            php_converter_toUCallback_arginfo,       ZEND_ACC_PUBLIC)
1017    PHP_ME(UConverter, fromUCallback,          php_converter_fromUCallback_arginfo,     ZEND_ACC_PUBLIC)
1018
1019    /* Core conversion workhorses */
1020    PHP_ME(UConverter, convert,                php_converter_convert_arginfo,           ZEND_ACC_PUBLIC)
1021    PHP_ME(UConverter, transcode,              php_converter_transcode_arginfo,         ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
1022
1023    /* Error inspection */
1024    PHP_ME(UConverter, getErrorCode,           php_converter_geterrorcode_arginfo,      ZEND_ACC_PUBLIC)
1025    PHP_ME(UConverter, getErrorMessage,        php_converter_geterrormsg_arginfo,       ZEND_ACC_PUBLIC)
1026
1027    /* Ennumeration and lookup */
1028    PHP_ME(UConverter, reasonText,             php_converter_reasontext_arginfo,        ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
1029    PHP_ME(UConverter, getAvailable,           php_converter_getavailable_arginfo,      ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
1030    PHP_ME(UConverter, getAliases,             php_converter_getaliases_arginfo,        ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
1031    PHP_ME(UConverter, getStandards,           php_converter_getstandards_arginfo,      ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
1032    { NULL, NULL, NULL }
1033};
1034
1035/* {{{ Converter create/clone/destroy */
1036static void php_converter_free_object(php_converter_object *objval TSRMLS_DC) {
1037    if (objval->src) {
1038        ucnv_close(objval->src);
1039    }
1040
1041    if (objval->dest) {
1042        ucnv_close(objval->dest);
1043    }
1044
1045    intl_error_reset(&(objval->error) TSRMLS_CC);
1046    zend_object_std_dtor(&(objval->obj) TSRMLS_CC);
1047
1048    efree(objval);
1049}
1050
1051static zend_object_value php_converter_object_ctor(zend_class_entry *ce, php_converter_object **pobjval TSRMLS_DC) {
1052    php_converter_object *objval;
1053    zend_object_value retval;
1054
1055    objval = ecalloc(1, sizeof(php_converter_object));
1056    objval->obj.ce = ce;
1057
1058#ifdef ZTS
1059    objval->tsrm_ls = TSRMLS_C;
1060#endif
1061    intl_error_init(&(objval->error) TSRMLS_CC);
1062
1063    retval.handle = zend_objects_store_put(objval, NULL, (zend_objects_free_object_storage_t)php_converter_free_object, NULL TSRMLS_CC);
1064    retval.handlers = &php_converter_object_handlers;
1065    *pobjval = objval;
1066
1067    return retval;
1068}
1069
1070static zend_object_value php_converter_create_object(zend_class_entry *ce TSRMLS_DC) {
1071    php_converter_object *objval = NULL;
1072    zend_object_value retval = php_converter_object_ctor(ce, &objval TSRMLS_CC);
1073
1074    object_properties_init(&(objval->obj), ce);
1075
1076    return retval;
1077}
1078
1079static zend_object_value php_converter_clone_object(zval *object TSRMLS_DC) {
1080    php_converter_object *objval, *oldobj = (php_converter_object*)zend_objects_get_address(object TSRMLS_CC);
1081    zend_object_value retval = php_converter_object_ctor(Z_OBJCE_P(object), &objval TSRMLS_CC);
1082    UErrorCode error = U_ZERO_ERROR;
1083
1084    intl_errors_reset(&oldobj->error TSRMLS_CC);
1085
1086    objval->src = ucnv_safeClone(oldobj->src, NULL, NULL, &error);
1087    if (U_SUCCESS(error)) {
1088        error = U_ZERO_ERROR;
1089        objval->dest = ucnv_safeClone(oldobj->dest, NULL, NULL, &error);
1090    }
1091    if (U_FAILURE(error)) {
1092        char *err_msg;
1093        THROW_UFAILURE(oldobj, "ucnv_safeClone", error);
1094
1095        err_msg = intl_error_get_message(&oldobj->error TSRMLS_CC);
1096        zend_throw_exception(NULL, err_msg, 0 TSRMLS_CC);
1097        efree(err_msg);
1098
1099        return retval;
1100    }
1101
1102    /* Update contexts for converter error handlers */
1103    php_converter_set_callbacks(objval, objval->src  TSRMLS_CC);
1104    php_converter_set_callbacks(objval, objval->dest TSRMLS_CC);
1105
1106    zend_objects_clone_members(&(objval->obj), retval, &(oldobj->obj), Z_OBJ_HANDLE_P(object) TSRMLS_CC);
1107
1108    /* Newly cloned object deliberately does not inherit error state from original object */
1109
1110    return retval;
1111}
1112/* }}} */
1113
1114#define CONV_REASON_CONST(v) zend_declare_class_constant_long(php_converter_ce, "REASON_" #v, sizeof("REASON_" #v) - 1, UCNV_ ## v TSRMLS_CC)
1115#define CONV_TYPE_CONST(v)   zend_declare_class_constant_long(php_converter_ce, #v ,          sizeof(#v) - 1,           UCNV_ ## v TSRMLS_CC)
1116
1117/* {{{ php_converter_minit */
1118int php_converter_minit(INIT_FUNC_ARGS) {
1119    zend_class_entry ce;
1120
1121    INIT_CLASS_ENTRY(ce, "UConverter", php_converter_methods);
1122    php_converter_ce = zend_register_internal_class(&ce TSRMLS_CC);
1123    php_converter_ce->create_object = php_converter_create_object;
1124    memcpy(&php_converter_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
1125    php_converter_object_handlers.clone_obj = php_converter_clone_object;
1126
1127    /* enum UConverterCallbackReason */
1128    CONV_REASON_CONST(UNASSIGNED);
1129    CONV_REASON_CONST(ILLEGAL);
1130    CONV_REASON_CONST(IRREGULAR);
1131    CONV_REASON_CONST(RESET);
1132    CONV_REASON_CONST(CLOSE);
1133    CONV_REASON_CONST(CLONE);
1134
1135    /* enum UConverterType */
1136    CONV_TYPE_CONST(UNSUPPORTED_CONVERTER);
1137    CONV_TYPE_CONST(SBCS);
1138    CONV_TYPE_CONST(DBCS);
1139    CONV_TYPE_CONST(MBCS);
1140    CONV_TYPE_CONST(LATIN_1);
1141    CONV_TYPE_CONST(UTF8);
1142    CONV_TYPE_CONST(UTF16_BigEndian);
1143    CONV_TYPE_CONST(UTF16_LittleEndian);
1144    CONV_TYPE_CONST(UTF32_BigEndian);
1145    CONV_TYPE_CONST(UTF32_LittleEndian);
1146    CONV_TYPE_CONST(EBCDIC_STATEFUL);
1147    CONV_TYPE_CONST(ISO_2022);
1148    CONV_TYPE_CONST(LMBCS_1);
1149    CONV_TYPE_CONST(LMBCS_2);
1150    CONV_TYPE_CONST(LMBCS_3);
1151    CONV_TYPE_CONST(LMBCS_4);
1152    CONV_TYPE_CONST(LMBCS_5);
1153    CONV_TYPE_CONST(LMBCS_6);
1154    CONV_TYPE_CONST(LMBCS_8);
1155    CONV_TYPE_CONST(LMBCS_11);
1156    CONV_TYPE_CONST(LMBCS_16);
1157    CONV_TYPE_CONST(LMBCS_17);
1158    CONV_TYPE_CONST(LMBCS_18);
1159    CONV_TYPE_CONST(LMBCS_19);
1160    CONV_TYPE_CONST(LMBCS_LAST);
1161    CONV_TYPE_CONST(HZ);
1162    CONV_TYPE_CONST(SCSU);
1163    CONV_TYPE_CONST(ISCII);
1164    CONV_TYPE_CONST(US_ASCII);
1165    CONV_TYPE_CONST(UTF7);
1166    CONV_TYPE_CONST(BOCU1);
1167    CONV_TYPE_CONST(UTF16);
1168    CONV_TYPE_CONST(UTF32);
1169    CONV_TYPE_CONST(CESU8);
1170    CONV_TYPE_CONST(IMAP_MAILBOX);
1171
1172    return SUCCESS;
1173}
1174/* }}} */
1175
1176/*
1177 * Local variables:
1178 * tab-width: 4
1179 * c-basic-offset: 4
1180 * End:
1181 * vim600: noet sw=4 ts=4 fdm=marker
1182 * vim<600: noet sw=4 ts=4
1183 */
1184