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