diff options
Diffstat (limited to 'contrib/idn/idnkit-1.0-src/lib/res.c')
-rw-r--r-- | contrib/idn/idnkit-1.0-src/lib/res.c | 1726 |
1 files changed, 1726 insertions, 0 deletions
diff --git a/contrib/idn/idnkit-1.0-src/lib/res.c b/contrib/idn/idnkit-1.0-src/lib/res.c new file mode 100644 index 0000000..cefd0c3 --- /dev/null +++ b/contrib/idn/idnkit-1.0-src/lib/res.c @@ -0,0 +1,1726 @@ +#ifndef lint +static char *rcsid = "$Id: res.c,v 1.1.1.1 2003/06/04 00:26:10 marka Exp $"; +#endif + +/* + * Copyright (c) 2000,2002 Japan Network Information Center. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set forth bellow. + * + * LICENSE TERMS AND CONDITIONS + * + * The following License Terms and Conditions apply, unless a different + * license is obtained from Japan Network Information Center ("JPNIC"), + * a Japanese association, Kokusai-Kougyou-Kanda Bldg 6F, 2-3-4 Uchi-Kanda, + * Chiyoda-ku, Tokyo 101-0047, Japan. + * + * 1. Use, Modification and Redistribution (including distribution of any + * modified or derived work) in source and/or binary forms is permitted + * under this License Terms and Conditions. + * + * 2. Redistribution of source code must retain the copyright notices as they + * appear in each source code file, this License Terms and Conditions. + * + * 3. Redistribution in binary form must reproduce the Copyright Notice, + * this License Terms and Conditions, in the documentation and/or other + * materials provided with the distribution. For the purposes of binary + * distribution the "Copyright Notice" refers to the following language: + * "Copyright (c) 2000-2002 Japan Network Information Center. All rights reserved." + * + * 4. The name of JPNIC may not be used to endorse or promote products + * derived from this Software without specific prior written approval of + * JPNIC. + * + * 5. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY JPNIC + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JPNIC BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <config.h> + +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +#include <idn/result.h> +#include <idn/assert.h> +#include <idn/logmacro.h> +#include <idn/converter.h> +#include <idn/normalizer.h> +#include <idn/checker.h> +#include <idn/mapper.h> +#include <idn/mapselector.h> +#include <idn/delimitermap.h> +#include <idn/resconf.h> +#include <idn/res.h> +#include <idn/util.h> +#include <idn/debug.h> +#include <idn/ucs4.h> + +#ifndef IDN_UTF8_ENCODING_NAME +#define IDN_UTF8_ENCODING_NAME "UTF-8" /* by IANA */ +#endif + +#ifndef WITHOUT_ICONV +#define ENCODE_MASK \ + (IDN_LOCALCONV | IDN_DELIMMAP | IDN_LOCALMAP | IDN_MAP | \ + IDN_NORMALIZE | IDN_PROHCHECK | IDN_UNASCHECK | IDN_BIDICHECK | \ + IDN_ASCCHECK | IDN_IDNCONV | IDN_LENCHECK | IDN_ENCODE_QUERY | \ + IDN_UNDOIFERR) +#define DECODE_MASK \ + (IDN_DELIMMAP | IDN_MAP | IDN_NORMALIZE | IDN_PROHCHECK | \ + IDN_UNASCHECK | IDN_BIDICHECK | IDN_IDNCONV | IDN_ASCCHECK | \ + IDN_RTCHECK | IDN_LOCALCONV | IDN_DECODE_QUERY) +#else +#define ENCODE_MASK \ + (IDN_DELIMMAP | IDN_LOCALMAP | IDN_MAP | IDN_NORMALIZE | \ + IDN_PROHCHECK | IDN_UNASCHECK | IDN_BIDICHECK | IDN_ASCCHECK | \ + IDN_IDNCONV | IDN_LENCHECK | IDN_ENCODE_QUERY | IDN_UNDOIFERR) +#define DECODE_MASK \ + (IDN_DELIMMAP | IDN_MAP | IDN_NORMALIZE | IDN_PROHCHECK | \ + IDN_UNASCHECK | IDN_BIDICHECK | IDN_IDNCONV | IDN_ASCCHECK | \ + IDN_RTCHECK | IDN_DECODE_QUERY) +#endif + +#define MAX_LABEL_LENGTH 63 + +/* + * label to convert. + */ +typedef struct labellist * labellist_t; +struct labellist { + unsigned long *name; + size_t name_length; + unsigned long *undo_name; + labellist_t next; + labellist_t previous; + int dot_followed; +}; + +typedef idn_result_t (*res_insnproc_t)(idn_resconf_t ctx, + labellist_t label); + +static void idn_res_initialize(void); +static idn_result_t copy_verbatim(const char *from, char *to, + size_t tolen); +static idn_result_t labellist_create(const unsigned long *name, + labellist_t *labelp); +static void labellist_destroy(labellist_t label); +static idn_result_t labellist_setname(labellist_t label, + const unsigned long *name); +static const unsigned long * + labellist_getname(labellist_t label); +static const unsigned long * + labellist_gettldname(labellist_t label); +static idn_result_t labellist_getnamelist(labellist_t label, + unsigned long *name, + size_t label_length); +static void labellist_undo(labellist_t label); +static labellist_t labellist_tail(labellist_t label); +static labellist_t labellist_previous(labellist_t label); + +#ifndef WITHOUT_ICONV +static idn_result_t label_localdecodecheck(idn_resconf_t ctx, + labellist_t label); +#endif +static idn_result_t label_idnencode_ace(idn_resconf_t ctx, + labellist_t label); +static idn_result_t label_idndecode(idn_resconf_t ctx, labellist_t label); +static idn_result_t label_localmap(idn_resconf_t ctx, labellist_t label); +static idn_result_t label_map(idn_resconf_t ctx, labellist_t label); +static idn_result_t label_normalize(idn_resconf_t ctx, labellist_t label); +static idn_result_t label_prohcheck(idn_resconf_t ctx, labellist_t label); +static idn_result_t label_unascheck(idn_resconf_t ctx, labellist_t label); +static idn_result_t label_bidicheck(idn_resconf_t ctx, labellist_t label); +static idn_result_t label_asccheck(idn_resconf_t ctx, labellist_t label); +static idn_result_t label_lencheck_ace(idn_resconf_t ctx, + labellist_t label); +static idn_result_t label_lencheck_nonace(idn_resconf_t ctx, + labellist_t label); +static idn_result_t label_rtcheck(idn_resconf_t ctx, idn_action_t actions, + labellist_t label, + const unsigned long *original_name); + +static int initialized; +static int enabled; + +void +idn_res_enable(int on_off) { + if (!initialized) { + idn_res_initialize(); + } + + if (on_off == 0) { + enabled = 0; + } else { + enabled = 1; + } +} + +static void +idn_res_initialize(void) { + if (!initialized) { + char *value = getenv("IDN_DISABLE"); + + if (value == NULL) { + enabled = 1; + } else { + enabled = 0; + } + initialized = 1; + } +} + +idn_result_t +idn_res_encodename(idn_resconf_t ctx, idn_action_t actions, const char *from, + char *to, size_t tolen) { + idn_converter_t local_converter = NULL; + idn_converter_t idn_converter = NULL; + idn_delimitermap_t delimiter_mapper; + idn_result_t r; + labellist_t labels = NULL, l; + unsigned long *buffer = NULL; + size_t buffer_length; + int from_is_root; + int idn_is_ace; + + assert(ctx != NULL && from != NULL && to != NULL); + + TRACE(("idn_res_encodename(actions=%s, from=\"%s\", tolen=%d)\n", + idn__res_actionstostring(actions), + idn__debug_xstring(from, 50), (int)tolen)); + + if (actions & ~ENCODE_MASK) { + WARNING(("idn_res_encodename: invalid actions 0x%x\n", + actions)); + r = idn_invalid_action; + goto ret; + } + + if (!initialized) + idn_res_initialize(); + if (!enabled || actions == 0) { + r = copy_verbatim(from, to, tolen); + goto ret; + } else if (tolen <= 0) { + r = idn_buffer_overflow; + goto ret; + } + + if (actions & IDN_ENCODE_QUERY) { +#ifndef WITHOUT_ICONV + actions |= (IDN_LOCALCONV | IDN_DELIMMAP | IDN_LOCALMAP | \ + IDN_MAP | IDN_NORMALIZE | IDN_PROHCHECK | \ + IDN_BIDICHECK | IDN_IDNCONV | IDN_LENCHECK); +#else + actions |= (IDN_DELIMMAP | IDN_LOCALMAP | IDN_MAP | \ + IDN_NORMALIZE | IDN_PROHCHECK | IDN_BIDICHECK | \ + IDN_IDNCONV | IDN_LENCHECK); +#endif + } + + /* + * Convert `from' to UCS4. + */ + local_converter = idn_resconf_getlocalconverter(ctx); +#ifndef WITHOUT_ICONV + if (local_converter == NULL) { + r = idn_invalid_name; + goto ret; + } +#endif + + idn_converter = idn_resconf_getidnconverter(ctx); + if (idn_converter != NULL && + idn_converter_isasciicompatible(idn_converter)) + idn_is_ace = 1; + else + idn_is_ace = 0; + + buffer_length = tolen * 2; + + for (;;) { + void *new_buffer; + + new_buffer = realloc(buffer, sizeof(*buffer) * buffer_length); + if (new_buffer == NULL) { + r = idn_nomemory; + goto ret; + } + buffer = (unsigned long *)new_buffer; + + if (actions & IDN_LOCALCONV) { + r = idn_converter_convtoucs4(local_converter, from, + buffer, buffer_length); + } else { + r = idn_ucs4_utf8toucs4(from, buffer, buffer_length); + } + if (r == idn_success) + break; + else if (r != idn_buffer_overflow) + goto ret; + + buffer_length *= 2; + } + + if (*buffer == '\0') { + if (tolen <= 0) { + r = idn_buffer_overflow; + goto ret; + } + *to = '\0'; + r = idn_success; + goto ret; + } + + /* + * Delimiter map. + */ + if (actions & IDN_DELIMMAP) { + TRACE(("res delimitermap(name=\"%s\")\n", + idn__debug_ucs4xstring(buffer, 50))); + + delimiter_mapper = idn_resconf_getdelimitermap(ctx); + if (delimiter_mapper != NULL) { + r = idn_delimitermap_map(delimiter_mapper, buffer, + buffer, buffer_length); + idn_delimitermap_destroy(delimiter_mapper); + if (r != idn_success) + goto ret; + } + TRACE(("res delimitermap(): success (name=\"%s\")\n", + idn__debug_ucs4xstring(buffer, 50))); + } + + from_is_root = (buffer[0] == '.' && buffer[1] == '\0'); + + /* + * Split the name into a list of labels. + */ + r = labellist_create(buffer, &labels); + if (r != idn_success) + goto ret; + + /* + * Perform conversions and tests. + */ + for (l = labellist_tail(labels); l != NULL; + l = labellist_previous(l)) { + + if (!idn__util_ucs4isasciirange(labellist_getname(l))) { + if (actions & IDN_LOCALMAP) { + r = label_localmap(ctx, l); + if (r != idn_success) + goto ret; + } + } + + if (!idn__util_ucs4isasciirange(labellist_getname(l))) { + if (actions & IDN_MAP) { + r = label_map(ctx, l); + if (r != idn_success) + goto ret; + } + if (actions & IDN_NORMALIZE) { + r = label_normalize(ctx, l); + if (r != idn_success) + goto ret; + } + if (actions & IDN_PROHCHECK) { + r = label_prohcheck(ctx, l); + if (r == idn_prohibited && + (actions & IDN_UNDOIFERR)) { + labellist_undo(l); + continue; + } else if (r != idn_success) { + goto ret; + } + } + if (actions & IDN_UNASCHECK) { + r = label_unascheck(ctx, l); + if (r == idn_prohibited && + (actions & IDN_UNDOIFERR)) { + labellist_undo(l); + continue; + } else if (r != idn_success) { + goto ret; + } + } + if (actions & IDN_BIDICHECK) { + r = label_bidicheck(ctx, l); + if (r == idn_prohibited && + (actions & IDN_UNDOIFERR)) { + labellist_undo(l); + continue; + } else if (r != idn_success) { + goto ret; + } + } + } + + if (actions & IDN_ASCCHECK) { + r = label_asccheck(ctx, l); + if (r == idn_prohibited && (actions & IDN_UNDOIFERR)) { + labellist_undo(l); + continue; + } else if (r != idn_success) { + goto ret; + } + } + + if (!idn__util_ucs4isasciirange(labellist_getname(l))) { + if ((actions & IDN_IDNCONV) && idn_is_ace) { + r = label_idnencode_ace(ctx, l); + if (r != idn_success) + goto ret; + } + } + + if (!from_is_root && (actions & IDN_LENCHECK)) { + if (idn_is_ace) + r = label_lencheck_ace(ctx, l); + else + r = label_lencheck_nonace(ctx, l); + if (r == idn_invalid_length && + (actions & IDN_UNDOIFERR)) { + labellist_undo(l); + continue; + } else if (r != idn_success) { + goto ret; + } + } + } + + /* + * Concat a list of labels to a name. + */ + for (;;) { + void *new_buffer; + + new_buffer = realloc(buffer, sizeof(*buffer) * buffer_length); + if (new_buffer == NULL) { + r = idn_nomemory; + goto ret; + } + buffer = (unsigned long *)new_buffer; + + r = labellist_getnamelist(labels, buffer, buffer_length); + if (r == idn_success) + break; + else if (r != idn_buffer_overflow) + goto ret; + + buffer_length *= 2; + } + + if ((actions & IDN_IDNCONV) && idn_converter != NULL && !idn_is_ace) { + r = idn_converter_convfromucs4(idn_converter, buffer, to, + tolen); + } else { + r = idn_ucs4_ucs4toutf8(buffer, to, tolen); + } + +ret: + if (r == idn_success) { + TRACE(("idn_res_encodename(): success (to=\"%s\")\n", + idn__debug_xstring(to, 50))); + } else { + TRACE(("idn_res_encodename(): %s\n", idn_result_tostring(r))); + } + free(buffer); + if (local_converter != NULL) + idn_converter_destroy(local_converter); + if (idn_converter != NULL) + idn_converter_destroy(idn_converter); + if (labels != NULL) + labellist_destroy(labels); + return (r); +} + +idn_result_t +idn_res_decodename(idn_resconf_t ctx, idn_action_t actions, const char *from, + char *to, size_t tolen) { + idn_converter_t local_converter = NULL; + idn_converter_t idn_converter = NULL; + idn_delimitermap_t delimiter_mapper; + idn_result_t r; + labellist_t labels = NULL, l; + unsigned long *buffer = NULL; + unsigned long *saved_name = NULL; + size_t buffer_length; + int idn_is_ace; + + assert(ctx != NULL && from != NULL && to != NULL); + + TRACE(("idn_res_decodename(actions=%s, from=\"%s\", tolen=%d)\n", + idn__res_actionstostring(actions), + idn__debug_xstring(from, 50), (int)tolen)); + + if (actions & ~DECODE_MASK) { + WARNING(("idn_res_decodename: invalid actions 0x%x\n", + actions)); + r = idn_invalid_action; + goto ret; + } + + if (!initialized) + idn_res_initialize(); + if (!enabled || actions == 0) { + r = copy_verbatim(from, to, tolen); + goto ret; + } else if (tolen <= 0) { + r = idn_buffer_overflow; + goto ret; + } + + if (actions & IDN_DECODE_QUERY) { +#ifndef WITHOUT_ICONV + actions |= (IDN_DELIMMAP | IDN_MAP | IDN_NORMALIZE | \ + IDN_PROHCHECK | IDN_BIDICHECK | IDN_IDNCONV | \ + IDN_RTCHECK | IDN_LOCALCONV); +#else + actions |= (IDN_DELIMMAP | IDN_MAP | IDN_NORMALIZE | \ + IDN_PROHCHECK | IDN_BIDICHECK | IDN_IDNCONV | \ + IDN_RTCHECK); +#endif + } + + /* + * Convert `from' to UCS4. + */ + local_converter = idn_resconf_getlocalconverter(ctx); +#ifndef WITHOUT_ICONV + if (local_converter == NULL) { + r = idn_invalid_name; + goto ret; + } +#endif + + idn_converter = idn_resconf_getidnconverter(ctx); + if (idn_converter != NULL && + idn_converter_isasciicompatible(idn_converter)) + idn_is_ace = 1; + else + idn_is_ace = 0; + + buffer_length = tolen * 2; + + TRACE(("res idndecode(name=\"%s\")\n", idn__debug_xstring(from, 50))); + + for (;;) { + void *new_buffer; + + new_buffer = realloc(buffer, sizeof(*buffer) * buffer_length); + if (new_buffer == NULL) { + r = idn_nomemory; + goto ret; + } + buffer = (unsigned long *)new_buffer; + + if ((actions & IDN_IDNCONV) && + idn_converter != NULL && !idn_is_ace) { + r = idn_converter_convtoucs4(idn_converter, from, + buffer, buffer_length); + } else { + r = idn_ucs4_utf8toucs4(from, buffer, buffer_length); + } + if (r == idn_success) + break; + else if (r != idn_buffer_overflow) + goto ret; + + buffer_length *= 2; + } + + if (*buffer == '\0') { + if (tolen <= 0) { + r = idn_buffer_overflow; + goto ret; + } + *to = '\0'; + r = idn_success; + goto ret; + } + + /* + * Delimiter map. + */ + if (actions & IDN_DELIMMAP) { + TRACE(("res delimitermap(name=\"%s\")\n", + idn__debug_ucs4xstring(buffer, 50))); + + delimiter_mapper = idn_resconf_getdelimitermap(ctx); + if (delimiter_mapper != NULL) { + r = idn_delimitermap_map(delimiter_mapper, buffer, + buffer, buffer_length); + idn_delimitermap_destroy(delimiter_mapper); + if (r != idn_success) + goto ret; + } + TRACE(("res delimitermap(): success (name=\"%s\")\n", + idn__debug_ucs4xstring(buffer, 50))); + } + + /* + * Split the name into a list of labels. + */ + r = labellist_create(buffer, &labels); + if (r != idn_success) + goto ret; + + /* + * Perform conversions and tests. + */ + for (l = labellist_tail(labels); l != NULL; + l = labellist_previous(l)) { + + free(saved_name); + saved_name = NULL; + + if (!idn__util_ucs4isasciirange(labellist_getname(l))) { + if (actions & IDN_MAP) { + r = label_map(ctx, l); + if (r != idn_success) + goto ret; + } + if (actions & IDN_NORMALIZE) { + r = label_normalize(ctx, l); + if (r != idn_success) + goto ret; + } + if (actions & IDN_PROHCHECK) { + r = label_prohcheck(ctx, l); + if (r == idn_prohibited) { + labellist_undo(l); + continue; + } else if (r != idn_success) { + goto ret; + } + } + if (actions & IDN_UNASCHECK) { + r = label_unascheck(ctx, l); + if (r == idn_prohibited) { + labellist_undo(l); + continue; + } else if (r != idn_success) { + goto ret; + } + } + if (actions & IDN_BIDICHECK) { + r = label_bidicheck(ctx, l); + if (r == idn_prohibited) { + labellist_undo(l); + continue; + } else if (r != idn_success) { + goto ret; + } + } + } + + if ((actions & IDN_IDNCONV) && idn_is_ace) { + saved_name = idn_ucs4_strdup(labellist_getname(l)); + if (saved_name == NULL) { + r = idn_nomemory; + goto ret; + } + r = label_idndecode(ctx, l); + if (r == idn_invalid_encoding) { + labellist_undo(l); + continue; + } else if (r != idn_success) { + goto ret; + } + } + if ((actions & IDN_RTCHECK) && saved_name != NULL) { + r = label_rtcheck(ctx, actions, l, saved_name); + if (r == idn_invalid_encoding) { + labellist_undo(l); + continue; + } else if (r != idn_success) { + goto ret; + } + } + +#ifndef WITHOUT_ICONV + if (actions & IDN_LOCALCONV) { + r = label_localdecodecheck(ctx, l); + if (r != idn_success) + goto ret; + } +#endif + } + + /* + * Concat a list of labels to a name. + */ + for (;;) { + void *new_buffer; + + new_buffer = realloc(buffer, sizeof(*buffer) * buffer_length); + if (new_buffer == NULL) { + r = idn_nomemory; + goto ret; + } + buffer = (unsigned long *)new_buffer; + + r = labellist_getnamelist(labels, buffer, buffer_length); + if (r == idn_success) + break; + else if (r != idn_buffer_overflow) + goto ret; + + buffer_length *= 2; + } + + if (actions & IDN_LOCALCONV) { + r = idn_converter_convfromucs4(local_converter, buffer, to, + tolen); + } else { + r = idn_ucs4_ucs4toutf8(buffer, to, tolen); + } + +ret: + if (r == idn_success) { + TRACE(("idn_res_decodename(): success (to=\"%s\")\n", + idn__debug_xstring(to, 50))); + } else { + TRACE(("idn_res_decodename(): %s\n", idn_result_tostring(r))); + } + free(saved_name); + free(buffer); + if (local_converter != NULL) + idn_converter_destroy(local_converter); + if (idn_converter != NULL) + idn_converter_destroy(idn_converter); + if (labels != NULL) + labellist_destroy(labels); + return (r); +} + +idn_result_t +idn_res_decodename2(idn_resconf_t ctx, idn_action_t actions, const char *from, + char *to, size_t tolen, const char *auxencoding) { +#ifdef WITHOUT_ICONV + return idn_failure; + +#else /* WITHOUT_ICONV */ + idn_result_t r; + idn_converter_t aux_converter = NULL; + unsigned long *buffer_ucs4 = NULL; + char *buffer_utf8 = NULL; + size_t buffer_length; + + assert(ctx != NULL && from != NULL && to != NULL); + + TRACE(("idn_res_decodename2(actions=%s, from=\"%s\", tolen=%d, " + "auxencoding=\"%s\")\n", + idn__res_actionstostring(actions), + idn__debug_xstring(from, 50), (int)tolen, + (auxencoding != NULL) ? auxencoding : "<null>")); + + if (!initialized) + idn_res_initialize(); + if (!enabled || actions == 0) { + r = copy_verbatim(from, to, tolen); + goto ret; + } else if (tolen <= 0) { + r = idn_buffer_overflow; + goto ret; + } + + if (auxencoding == NULL || + strcmp(auxencoding, IDN_UTF8_ENCODING_NAME) == 0 || + strcmp(auxencoding, "UTF-8") == 0) { + return idn_res_decodename(ctx, actions, from, to, tolen); + } + + /* + * Convert `from' to UCS4. + */ + r = idn_resconf_setauxidnconvertername(ctx, auxencoding, + IDN_CONVERTER_DELAYEDOPEN); + if (r != idn_success) { + goto ret; + } + + aux_converter = idn_resconf_getauxidnconverter(ctx); + if (aux_converter == NULL) { + r = idn_failure; + goto ret; + } + + buffer_length = tolen * 2; + for (;;) { + void *new_buffer; + + new_buffer = realloc(buffer_ucs4, + sizeof(*buffer_ucs4) * buffer_length); + if (new_buffer == NULL) { + r = idn_nomemory; + goto ret; + } + buffer_ucs4 = (unsigned long *)new_buffer; + + r = idn_converter_convtoucs4(aux_converter, from, + buffer_ucs4, + buffer_length); + if (r == idn_success) + break; + else if (r != idn_buffer_overflow) + goto ret; + + buffer_length *= 2; + } + + if (*buffer_ucs4 == '\0') { + if (tolen <= 0) { + r = idn_buffer_overflow; + goto ret; + } + *to = '\0'; + r = idn_success; + goto ret; + } + + /* + * Convert `buffer_ucs4' to UTF-8. + */ + buffer_length = tolen * 2; + for (;;) { + void *new_buffer; + + new_buffer = realloc(buffer_utf8, + sizeof(*buffer_utf8) * buffer_length); + if (new_buffer == NULL) { + r = idn_nomemory; + goto ret; + } + buffer_utf8 = (char *)new_buffer; + r = idn_ucs4_ucs4toutf8(buffer_ucs4, buffer_utf8, + buffer_length); + + if (r == idn_success) + break; + else if (r != idn_buffer_overflow) + goto ret; + + buffer_length *= 2; + } + + if (*buffer_utf8 == '\0') { + if (tolen <= 0) { + r = idn_buffer_overflow; + goto ret; + } + *to = '\0'; + r = idn_success; + goto ret; + } + + r = idn_res_decodename(ctx, actions, buffer_utf8, to, tolen); + +ret: + if (r == idn_success) { + TRACE(("idn_res_decodename2(): success (to=\"%s\")\n", + idn__debug_xstring(to, 50))); + } else { + TRACE(("idn_res_decodename2(): %s\n", idn_result_tostring(r))); + } + free(buffer_ucs4); + free(buffer_utf8); + if (aux_converter != NULL) + idn_converter_destroy(aux_converter); + + return (r); + +#endif /* WITHOUT_ICONV */ +} + +static idn_result_t +copy_verbatim(const char *from, char *to, size_t tolen) { + size_t fromlen = strlen(from); + + if (fromlen + 1 > tolen) + return (idn_buffer_overflow); + (void)memcpy(to, from, fromlen + 1); + return (idn_success); +} + +static idn_result_t +labellist_create(const unsigned long *name, labellist_t *labelp) { + size_t length, malloc_length; + labellist_t head_label = NULL; + labellist_t tail_label = NULL; + labellist_t new_label = NULL; + const unsigned long *endp = NULL; + idn_result_t r; + + while (*name != '\0') { + for (endp = name; *endp != '.' && *endp != '\0'; endp++) + ; /* nothing to be done */ + length = (endp - name) + 1; + malloc_length = length + 15; /* add 15 for margin */ + + new_label = (labellist_t) + malloc(sizeof(struct labellist)); + if (new_label == NULL) { + r = idn_nomemory; + goto ret; + } + if (head_label == NULL) + head_label = new_label; + + new_label->name = NULL; + new_label->undo_name = NULL; + new_label->name_length = malloc_length; + new_label->next = NULL; + new_label->previous = NULL; + new_label->dot_followed = (*endp == '.'); + + new_label->name = (unsigned long *) + malloc(sizeof(long) * malloc_length); + if (new_label->name == NULL) { + r = idn_nomemory; + goto ret; + } + memcpy(new_label->name, name, sizeof(long) * length); + *(new_label->name + length - 1) = '\0'; + + new_label->undo_name = (unsigned long *) + malloc(sizeof(long) * malloc_length); + if (new_label->undo_name == NULL) { + r = idn_nomemory; + goto ret; + } + memcpy(new_label->undo_name, name, sizeof(long) * length); + *(new_label->undo_name + length - 1) = '\0'; + + if (tail_label != NULL) { + tail_label->next = new_label; + new_label->previous = tail_label; + } + tail_label = new_label; + + if (*endp == '.') + name = endp + 1; + else + name = endp; + } + + *labelp = head_label; + r = idn_success; + +ret: + if (r != idn_success) { + if (new_label != NULL) { + free(new_label->name); + free(new_label->undo_name); + free(new_label); + } + if (head_label != NULL) + labellist_destroy(head_label); + } + return (r); +} + + +static void +labellist_destroy(labellist_t label) { + labellist_t l, l_next; + + for (l = label; l != NULL; l = l_next) { + l_next = l->next; + free(l->name); + free(l->undo_name); + free(l); + } +} + +static idn_result_t +labellist_setname(labellist_t label, const unsigned long *name) { + unsigned long *new_name; + size_t length, new_length; + + length = idn_ucs4_strlen(name) + 1; + new_length = length + 15; /* add 15 for margin */ + + if (label->name_length < new_length) { + new_name = (unsigned long *) + realloc(label->name, sizeof(long) * new_length); + if (new_name == NULL) + return (idn_nomemory); + label->name = new_name; + label->name_length = new_length; + } + memcpy(label->name, name, sizeof(long) * length); + + return (idn_success); +} + +static const unsigned long * +labellist_getname(labellist_t label) { + return (label->name); +} + +static const unsigned long * +labellist_gettldname(labellist_t label) { + labellist_t l; + + if (label->previous == NULL && label->next == NULL && + !label->dot_followed) + return (idn_mapselector_getnotld()); + + for (l = label; l->next != NULL; l = l->next) + ; /* nothing to be done */ + + return (l->name); +} + +static idn_result_t +labellist_getnamelist(labellist_t label, unsigned long *name, + size_t name_length) { + static const unsigned long dot_string[] = {0x002e, 0x0000}; /* "." */ + size_t length; + labellist_t l; + + for (l = label, length = 0; l != NULL; l = l->next) + length += idn_ucs4_strlen(l->name) + 1; /* name + `.' */ + length++; /* for NUL */ + + if (name_length < length) + return (idn_buffer_overflow); + + *name = '\0'; + for (l = label; l != NULL; l = l->next) { + idn_ucs4_strcat(name, l->name); + name += idn_ucs4_strlen(name); + if (l->dot_followed) + idn_ucs4_strcat(name, dot_string); + } + return (idn_success); +} + +static void +labellist_undo(labellist_t label) { + size_t length; + + length = idn_ucs4_strlen(label->undo_name) + 1; + memcpy(label->name, label->undo_name, sizeof(long) * length); +} + +static labellist_t +labellist_tail(labellist_t label) { + labellist_t l; + + if (label == NULL) + return (NULL); + for (l = label; l->next != NULL; l = l->next) + ; /* nothing to be done */ + return (l); +} + +static labellist_t +labellist_previous(labellist_t label) { + return (label->previous); +} + +#ifndef WITHOUT_ICONV + +static idn_result_t +label_localdecodecheck(idn_resconf_t ctx, labellist_t label) { + idn_converter_t local_converter = NULL; + const unsigned long *from; + char *to = NULL; + size_t to_length; + idn_result_t r; + + from = labellist_getname(label); + to_length = idn_ucs4_strlen(from) + 1 + 15; /* 15 for margin */ + TRACE(("res ucs4tolocal_check(label=\"%s\")\n", + idn__debug_ucs4xstring(from, 50))); + + local_converter = idn_resconf_getlocalconverter(ctx); + if (local_converter == NULL) { + r = idn_success; + goto ret; + } + + for (;;) { + char *new_buffer; + + new_buffer = (char *)realloc(to, to_length); + if (new_buffer == NULL) { + r = idn_nomemory; + goto ret; + } + to = new_buffer; + r = idn_converter_convfromucs4(local_converter, from, to, + to_length); + if (r == idn_success) + break; + else if (r == idn_nomapping) { + r = label_idnencode_ace(ctx, label); + if (r != idn_success) + goto ret; + break; + } else if (r != idn_buffer_overflow) { + goto ret; + } + to_length *= 2; + } + + r = idn_success; +ret: + TRACE(("res ucs4tolocal_check(): %s\n", idn_result_tostring(r))); + if (local_converter != NULL) + idn_converter_destroy(local_converter); + free(to); + return (r); +} + +#endif /* !WITHOUT_ICONV */ + +static idn_result_t +label_idndecode(idn_resconf_t ctx, labellist_t label) { + idn_converter_t idn_converter = NULL; + const unsigned long *from; + char *ascii_from = NULL; + unsigned long *to = NULL; + size_t from_length, to_length; + idn_result_t r; + + from = labellist_getname(label); + from_length = idn_ucs4_strlen(from) + 1; + TRACE(("res idntoucs4(label=\"%s\")\n", + idn__debug_ucs4xstring(from, 50))); + + idn_converter = idn_resconf_getidnconverter(ctx); + if (idn_converter == NULL) { + r = idn_success; + goto ret; + } + + for (;;) { + char *new_buffer; + + new_buffer = (char *) realloc(ascii_from, from_length); + if (new_buffer == NULL) { + r = idn_nomemory; + goto ret; + } + ascii_from = new_buffer; + r = idn_ucs4_ucs4toutf8(from, ascii_from, from_length); + if (r == idn_success) + break; + else if (r != idn_buffer_overflow) + goto ret; + from_length *= 2; + } + + to = NULL; + to_length = from_length; + + for (;;) { + unsigned long *new_buffer; + + new_buffer = (unsigned long *) + realloc(to, sizeof(long) * to_length); + if (new_buffer == NULL) { + r = idn_nomemory; + goto ret; + } + to = new_buffer; + r = idn_converter_convtoucs4(idn_converter, ascii_from, to, + to_length); + if (r == idn_success) + break; + else if (r != idn_buffer_overflow) + goto ret; + to_length *= 2; + } + + r = labellist_setname(label, to); +ret: + if (r == idn_success) { + TRACE(("res idntoucs4(): success (label=\"%s\")\n", + idn__debug_ucs4xstring(labellist_getname(label), + 50))); + } else { + TRACE(("res idntoucs4(): %s\n", idn_result_tostring(r))); + } + if (idn_converter != NULL) + idn_converter_destroy(idn_converter); + free(to); + free(ascii_from); + return (r); +} + +static idn_result_t +label_idnencode_ace(idn_resconf_t ctx, labellist_t label) { + idn_converter_t idn_converter = NULL; + const unsigned long *from; + char *ascii_to = NULL; + unsigned long *to = NULL; + size_t to_length; + idn_result_t r; + + from = labellist_getname(label); + TRACE(("res ucs4toidn(label=\"%s\")\n", + idn__debug_ucs4xstring(from, 50))); + + idn_converter = idn_resconf_getidnconverter(ctx); + if (idn_converter == NULL) { + r = idn_success; + goto ret; + } + + ascii_to = NULL; + to_length = idn_ucs4_strlen(from) * 4 + 16; /* add mergin */ + + for (;;) { + char *new_buffer; + + new_buffer = (char *) realloc(ascii_to, to_length); + if (new_buffer == NULL) { + r = idn_nomemory; + goto ret; + } + ascii_to = new_buffer; + r = idn_converter_convfromucs4(idn_converter, from, ascii_to, + to_length); + if (r == idn_success) + break; + else if (r != idn_buffer_overflow) + goto ret; + to_length *= 2; + } + + for (;;) { + unsigned long *new_buffer; + + new_buffer = (unsigned long *) + realloc(to, sizeof(long) * to_length); + if (new_buffer == NULL) { + r = idn_nomemory; + goto ret; + } + to = new_buffer; + r = idn_ucs4_utf8toucs4(ascii_to, to, to_length); + if (r == idn_success) + break; + else if (r != idn_buffer_overflow) + goto ret; + to_length *= 2; + } + + if (r != idn_success) + goto ret; + + r = labellist_setname(label, to); +ret: + if (r == idn_success) { + TRACE(("res ucs4toidn(): success (label=\"%s\")\n", + idn__debug_ucs4xstring(labellist_getname(label), + 50))); + } else { + TRACE(("res ucs4toidn(): %s\n", idn_result_tostring(r))); + } + if (idn_converter != NULL) + idn_converter_destroy(idn_converter); + free(to); + free(ascii_to); + return (r); +} + +static idn_result_t +label_localmap(idn_resconf_t ctx, labellist_t label) { + const unsigned long *from; + const unsigned long *tld; + unsigned long *to = NULL; + size_t to_length; + idn_mapselector_t local_mapper; + idn_result_t r; + + from = labellist_getname(label); + tld = labellist_gettldname(label); + TRACE(("res localmap(label=\"%s\", tld=\"%s\")\n", + idn__debug_ucs4xstring(from, 50), + idn__debug_ucs4xstring(tld, 50))); + + local_mapper = idn_resconf_getlocalmapselector(ctx); + if (local_mapper == NULL) { + r = idn_success; + goto ret; + } + + if (tld == from) + tld = idn_mapselector_getdefaulttld(); + to_length = idn_ucs4_strlen(from) + 1 + 15; /* 15 for margin */ + + for (;;) { + unsigned long *new_buffer; + + new_buffer = (unsigned long *) + realloc(to, sizeof(long) * to_length); + if (new_buffer == NULL) { + r = idn_nomemory; + goto ret; + } + to = new_buffer; + r = idn_mapselector_map2(local_mapper, from, tld, to, + to_length); + if (r == idn_success) + break; + else if (r != idn_buffer_overflow) + goto ret; + to_length *= 2; + } + + r = labellist_setname(label, to); +ret: + if (r == idn_success) { + TRACE(("res localmap(): success (label=\"%s\")\n", + idn__debug_ucs4xstring(labellist_getname(label), + 50))); + } else { + TRACE(("res localmap(): %s\n", idn_result_tostring(r))); + } + if (local_mapper != NULL) + idn_mapselector_destroy(local_mapper); + free(to); + return (r); +} + +static idn_result_t +label_map(idn_resconf_t ctx, labellist_t label) { + const unsigned long *from; + unsigned long *to = NULL; + size_t to_length; + idn_mapper_t mapper; + idn_result_t r; + + from = labellist_getname(label); + TRACE(("res map(label=\"%s\")\n", idn__debug_ucs4xstring(from, 50))); + + mapper = idn_resconf_getmapper(ctx); + if (mapper == NULL) { + r = idn_success; + goto ret; + } + to_length = idn_ucs4_strlen(from) + 1 + 15; /* 15 for margin */ + + for (;;) { + unsigned long *new_buffer; + + new_buffer = (unsigned long *) + realloc(to, sizeof(long) * to_length); + if (new_buffer == NULL) { + r = idn_nomemory; + goto ret; + } + to = new_buffer; + r = idn_mapper_map(mapper, from, to, to_length); + if (r == idn_success) + break; + else if (r != idn_buffer_overflow) + goto ret; + to_length *= 2; + } + + r = labellist_setname(label, to); +ret: + if (r == idn_success) { + TRACE(("res map(): success (label=\"%s\")\n", + idn__debug_ucs4xstring(labellist_getname(label), + 50))); + } else { + TRACE(("res map(): %s\n", idn_result_tostring(r))); + } + if (mapper != NULL) + idn_mapper_destroy(mapper); + free(to); + return (r); +} + +static idn_result_t +label_normalize(idn_resconf_t ctx, labellist_t label) { + const unsigned long *from; + unsigned long *to = NULL; + size_t to_length; + idn_normalizer_t normalizer; + idn_result_t r; + + from = labellist_getname(label); + TRACE(("res normalzie(label=\"%s\")\n", + idn__debug_ucs4xstring(from, 50))); + + normalizer = idn_resconf_getnormalizer(ctx); + if (normalizer == NULL) { + r = idn_success; + goto ret; + } + to_length = idn_ucs4_strlen(from) + 1 + 15; /* 15 for margin */ + + for (;;) { + unsigned long *new_buffer; + + new_buffer = (unsigned long *) + realloc(to, sizeof(long) * to_length); + if (new_buffer == NULL) { + r = idn_nomemory; + goto ret; + } + to = new_buffer; + r = idn_normalizer_normalize(normalizer, from, to, to_length); + if (r == idn_success) + break; + else if (r != idn_buffer_overflow) + goto ret; + to_length *= 2; + } + + r = labellist_setname(label, to); +ret: + if (r == idn_success) { + TRACE(("res normalize(): success (label=\"%s\")\n", + idn__debug_ucs4xstring(labellist_getname(label), + 50))); + } else { + TRACE(("res normalize(): %s\n", idn_result_tostring(r))); + } + if (normalizer != NULL) + idn_normalizer_destroy(normalizer); + free(to); + return (r); +} + +static idn_result_t +label_prohcheck(idn_resconf_t ctx, labellist_t label) { + const unsigned long *name, *found; + idn_checker_t prohibit_checker; + idn_result_t r; + + name = labellist_getname(label); + TRACE(("res prohcheck(label=\"%s\")\n", + idn__debug_ucs4xstring(name, 50))); + + prohibit_checker = idn_resconf_getprohibitchecker(ctx); + if (prohibit_checker == NULL) { + r = idn_success; + goto ret; + } + + r = idn_checker_lookup(prohibit_checker, name, &found); + idn_checker_destroy(prohibit_checker); + if (r == idn_success && found != NULL) + r = idn_prohibited; + +ret: + TRACE(("res prohcheck(): %s\n", idn_result_tostring(r))); + return (r); +} + +static idn_result_t +label_unascheck(idn_resconf_t ctx, labellist_t label) { + const unsigned long *name, *found; + idn_checker_t unassigned_checker; + idn_result_t r; + + name = labellist_getname(label); + TRACE(("res unascheck(label=\"%s\")\n", + idn__debug_ucs4xstring(name, 50))); + + unassigned_checker = idn_resconf_getunassignedchecker(ctx); + if (unassigned_checker == NULL) { + r = idn_success; + goto ret; + } + + r = idn_checker_lookup(unassigned_checker, name, &found); + idn_checker_destroy(unassigned_checker); + if (r == idn_success && found != NULL) + r = idn_prohibited; + +ret: + TRACE(("res unascheck(): %s\n", idn_result_tostring(r))); + return (r); +} + +static idn_result_t +label_bidicheck(idn_resconf_t ctx, labellist_t label) { + const unsigned long *name, *found; + idn_checker_t bidi_checker; + idn_result_t r; + + name = labellist_getname(label); + TRACE(("res bidicheck(label=\"%s\")\n", + idn__debug_ucs4xstring(name, 50))); + + bidi_checker = idn_resconf_getbidichecker(ctx); + if (bidi_checker == NULL) { + r = idn_success; + goto ret; + } + + r = idn_checker_lookup(bidi_checker, name, &found); + idn_checker_destroy(bidi_checker); + if (r == idn_success && found != NULL) + r = idn_prohibited; + +ret: + TRACE(("res bidicheck(): %s\n", idn_result_tostring(r))); + return (r); +} + +static idn_result_t +label_asccheck(idn_resconf_t ctx, labellist_t label) { + const unsigned long *name, *n; + idn_result_t r; + + name = labellist_getname(label); + TRACE(("res asccheck(label=\"%s\")\n", + idn__debug_ucs4xstring(name, 50))); + + if (*name == '-') { + r = idn_prohibited; + goto ret; + } + + for (n = name; *n != '\0'; n++) { + if (*n <= '\177') { + if ((*n < '0' || *n > '9') && + (*n < 'A' || *n > 'Z') && + (*n < 'a' || *n > 'z') && + *n != '-') { + r = idn_prohibited; + goto ret; + } + } + } + + if (n > name && *(n - 1) == '-') { + r = idn_prohibited; + goto ret; + } + + r = idn_success; +ret: + TRACE(("res asccheck(): %s\n", idn_result_tostring(r))); + return (r); +} + +static idn_result_t +label_lencheck_ace(idn_resconf_t ctx, labellist_t label) { + const unsigned long *name; + size_t name_length; + idn_result_t r; + + name = labellist_getname(label); + name_length = idn_ucs4_strlen(name); + TRACE(("res lencheck(label=\"%s\")\n", + idn__debug_ucs4xstring(name, 50))); + + if (name_length == 0 || name_length > MAX_LABEL_LENGTH) { + r = idn_invalid_length; + goto ret; + } + + r = idn_success; +ret: + TRACE(("res lencheck(): %s\n", idn_result_tostring(r))); + return (r); +} + +static idn_result_t +label_lencheck_nonace(idn_resconf_t ctx, labellist_t label) { + idn_converter_t idn_converter; + const unsigned long *from; + size_t to_length; + idn_result_t r; + char *buffer = NULL; + size_t buffer_length; + + from = labellist_getname(label); + TRACE(("res lencheck(label=\"%s\")\n", + idn__debug_ucs4xstring(from, 50))); + + buffer_length = idn_ucs4_strlen(from) * 4 + 16; /* 16 for margin */ + idn_converter = idn_resconf_getidnconverter(ctx); + + for (;;) { + void *new_buffer; + + new_buffer = realloc(buffer, sizeof(*buffer) * buffer_length); + if (new_buffer == NULL) { + r = idn_nomemory; + goto ret; + } + buffer = (char *)new_buffer; + + if (idn_converter != NULL) { + r = idn_converter_convfromucs4(idn_converter, from, + buffer, buffer_length); + } else { + r = idn_ucs4_ucs4toutf8(from, buffer, buffer_length); + } + if (r == idn_success) + break; + else if (r != idn_buffer_overflow) + goto ret; + + buffer_length *= 2; + } + + to_length = strlen(buffer); + if (to_length == 0 || to_length > MAX_LABEL_LENGTH) { + r = idn_invalid_length; + goto ret; + } + + r = idn_success; +ret: + TRACE(("res lencheck(): %s\n", idn_result_tostring(r))); + if (idn_converter != NULL) + idn_converter_destroy(idn_converter); + free(buffer); + return (r); +} + +static idn_result_t +label_rtcheck(idn_resconf_t ctx, idn_action_t actions, labellist_t label, + const unsigned long *original_name) { + labellist_t rt_label = NULL; + const unsigned long *rt_name; + const unsigned long *cur_name; + idn_result_t r; + + cur_name = labellist_getname(label); + TRACE(("res rtcheck(label=\"%s\", org_label=\"%s\")\n", + idn__debug_ucs4xstring(cur_name, 50), + idn__debug_ucs4xstring(original_name, 50))); + + r = labellist_create(cur_name, &rt_label); + if (r != idn_success) + goto ret; + if (rt_label == NULL) { + if (*original_name == '\0') + r = idn_success; + else + r = idn_invalid_encoding; + goto ret; + } + + if (!idn__util_ucs4isasciirange(labellist_getname(rt_label))) { + r = label_map(ctx, rt_label); + if (r != idn_success) + goto ret; + r = label_normalize(ctx, rt_label); + if (r != idn_success) + goto ret; + r = label_prohcheck(ctx, rt_label); + if (r != idn_success) + goto ret; + if (actions & IDN_UNASCHECK) { + r = label_unascheck(ctx, rt_label); + if (r != idn_success) + goto ret; + } + r = label_bidicheck(ctx, rt_label); + if (r != idn_success) + goto ret; + } + + if (actions & IDN_ASCCHECK) { + r = label_asccheck(ctx, rt_label); + if (r != idn_success) + goto ret; + } + if (!idn__util_ucs4isasciirange(labellist_getname(rt_label))) { + r = label_idnencode_ace(ctx, rt_label); + if (r != idn_success) + goto ret; + } + r = label_lencheck_ace(ctx, rt_label); + if (r != idn_success) + goto ret; + rt_name = labellist_getname(rt_label); + + if (idn_ucs4_strcasecmp(rt_name, original_name) != 0) { + TRACE(("res rtcheck(): round trip failed, org =\"%s\", rt=\"%s\"\n", + idn__debug_ucs4xstring(original_name, 50), + idn__debug_ucs4xstring(rt_name, 50))); + r = idn_invalid_encoding; + goto ret; + } + + r = idn_success; +ret: + if (r != idn_nomemory && r != idn_success) + r = idn_invalid_encoding; + TRACE(("res rtcheck(): %s\n", idn_result_tostring(r))); + if (rt_label != NULL) + labellist_destroy(rt_label); + return (r); +} + +const char * +idn__res_actionstostring(idn_action_t actions) { + static char buf[100]; + + buf[0] = '\0'; + + if (actions == IDN_ENCODE_QUERY) + strcpy(buf, "encode-query"); + else if (actions == IDN_DECODE_QUERY) + strcpy(buf, "decode-query"); + else if (actions == IDN_ENCODE_APP) + strcpy(buf, "encode-app"); + else if (actions == IDN_DECODE_APP) + strcpy(buf, "decode-app"); + else if (actions == IDN_ENCODE_STORED) + strcpy(buf, "encode-stored"); + else if (actions == IDN_DECODE_STORED) + strcpy(buf, "decode-stored"); + else { + if (actions & IDN_LOCALCONV) + strcat(buf, "|localconv"); + if (actions & IDN_DELIMMAP) + strcat(buf, "|delimmap"); + if (actions & IDN_LOCALMAP) + strcat(buf, "|localmap"); + + if (actions & IDN_MAP) + strcat(buf, "|map"); + if (actions & IDN_NORMALIZE) + strcat(buf, "|normalize"); + if (actions & IDN_PROHCHECK) + strcat(buf, "|prohcheck"); + if (actions & IDN_UNASCHECK) + strcat(buf, "|unascheck"); + if (actions & IDN_BIDICHECK) + strcat(buf, "|bidicheck"); + + if (actions & IDN_IDNCONV) + strcat(buf, "|idnconv"); + if (actions & IDN_ASCCHECK) + strcat(buf, "|asccheck"); + if (actions & IDN_LENCHECK) + strcat(buf, "|lencheck"); + if (actions & IDN_RTCHECK) + strcat(buf, "|rtcheck"); + } + + if (buf[0] == '|') + return (buf + 1); + else + return (buf); +} |