summaryrefslogtreecommitdiffstats
path: root/contrib/idn/idnkit-1.0-src/tools/idnconv/idnconv.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/idn/idnkit-1.0-src/tools/idnconv/idnconv.c')
-rw-r--r--contrib/idn/idnkit-1.0-src/tools/idnconv/idnconv.c803
1 files changed, 803 insertions, 0 deletions
diff --git a/contrib/idn/idnkit-1.0-src/tools/idnconv/idnconv.c b/contrib/idn/idnkit-1.0-src/tools/idnconv/idnconv.c
new file mode 100644
index 0000000..a10a738
--- /dev/null
+++ b/contrib/idn/idnkit-1.0-src/tools/idnconv/idnconv.c
@@ -0,0 +1,803 @@
+#ifndef lint
+static char *rcsid = "$Id: idnconv.c,v 1.1.1.1 2003/06/04 00:27:07 marka Exp $";
+#endif
+
+/*
+ * Copyright (c) 2000,2001,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.
+ */
+
+/*
+ * idnconv -- Codeset converter for named.conf and zone files
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
+#include <idn/result.h>
+#include <idn/converter.h>
+#include <idn/normalizer.h>
+#include <idn/utf8.h>
+#include <idn/resconf.h>
+#include <idn/res.h>
+#include <idn/util.h>
+#include <idn/version.h>
+
+#include "util.h"
+
+#define MAX_DELIMITER 10
+#define MAX_LOCALMAPPER 10
+#define MAX_MAPPER 10
+#define MAX_NORMALIZER 10
+#define MAX_CHEKER 10
+
+#define FLAG_REVERSE 0x0001
+#define FLAG_DELIMMAP 0x0002
+#define FLAG_LOCALMAP 0x0004
+#define FLAG_MAP 0x0008
+#define FLAG_NORMALIZE 0x0010
+#define FLAG_PROHIBITCHECK 0x0020
+#define FLAG_UNASSIGNCHECK 0x0040
+#define FLAG_BIDICHECK 0x0080
+#define FLAG_ASCIICHECK 0x0100
+#define FLAG_LENGTHCHECK 0x0200
+#define FLAG_ROUNDTRIPCHECK 0x0400
+#define FLAG_SELECTIVE 0x0800
+
+#define FLAG_NAMEPREP \
+ (FLAG_MAP|FLAG_NORMALIZE|FLAG_PROHIBITCHECK|FLAG_UNASSIGNCHECK|\
+ FLAG_BIDICHECK)
+
+#define DEFAULT_FLAGS \
+ (FLAG_LOCALMAP|FLAG_NAMEPREP|FLAG_ASCIICHECK|FLAG_LENGTHCHECK|\
+ FLAG_ROUNDTRIPCHECK|FLAG_SELECTIVE|FLAG_DELIMMAP)
+
+int line_number; /* current input file line number */
+static int flush_every_line = 0; /* pretty obvious */
+
+static int encode_file(idn_resconf_t conf1, idn_resconf_t conf2,
+ FILE *fp, int flags);
+static int decode_file(idn_resconf_t conf1, idn_resconf_t conf2,
+ FILE *fp, int flags);
+static int trim_newline(idnconv_strbuf_t *buf);
+static idn_result_t convert_line(idnconv_strbuf_t *from,
+ idnconv_strbuf_t *to,
+ idn_resconf_t conf,
+ idn_action_t actions, int flags);
+static void print_usage(char *cmd);
+static void print_version(void);
+static unsigned long get_ucs(const char *p);
+
+int
+main(int ac, char **av) {
+ char *cmd = *av;
+ char *cname;
+ unsigned long delimiters[MAX_DELIMITER];
+ char *localmappers[MAX_LOCALMAPPER];
+ char *nameprep_version = NULL;
+ int ndelimiters = 0;
+ int nlocalmappers = 0;
+ char *in_code = NULL;
+ char *out_code = NULL;
+ char *resconf_file = NULL;
+ int no_resconf = 0;
+ char *encoding_alias = NULL;
+ int flags = DEFAULT_FLAGS;
+ FILE *fp;
+ idn_result_t r;
+ idn_resconf_t resconf1, resconf2;
+ idn_converter_t conv;
+ int exit_value;
+
+#ifdef HAVE_SETLOCALE
+ (void)setlocale(LC_ALL, "");
+#endif
+
+ /*
+ * If the command name begins with 'r', reverse mode is assumed.
+ */
+ if ((cname = strrchr(cmd, '/')) != NULL)
+ cname++;
+ else
+ cname = cmd;
+ if (cname[0] == 'r')
+ flags |= FLAG_REVERSE;
+
+ ac--;
+ av++;
+ while (ac > 0 && **av == '-') {
+
+#define OPT_MATCH(opt) (strcmp(*av, opt) == 0)
+#define MUST_HAVE_ARG if (ac < 2) print_usage(cmd)
+#define APPEND_LIST(array, size, item, what) \
+ if (size >= (sizeof(array) / sizeof(array[0]))) { \
+ errormsg("too many " what "\n"); \
+ exit(1); \
+ } \
+ array[size++] = item; \
+ ac--; av++
+
+ if (OPT_MATCH("-in") || OPT_MATCH("-i")) {
+ MUST_HAVE_ARG;
+ in_code = av[1];
+ ac--;
+ av++;
+ } else if (OPT_MATCH("-out") || OPT_MATCH("-o")) {
+ MUST_HAVE_ARG;
+ out_code = av[1];
+ ac--;
+ av++;
+ } else if (OPT_MATCH("-conf") || OPT_MATCH("-c")) {
+ MUST_HAVE_ARG;
+ resconf_file = av[1];
+ ac--;
+ av++;
+ } else if (OPT_MATCH("-nameprep") || OPT_MATCH("-n")) {
+ MUST_HAVE_ARG;
+ nameprep_version = av[1];
+ ac--;
+ av++;
+ } else if (OPT_MATCH("-noconf") || OPT_MATCH("-C")) {
+ no_resconf = 1;
+ } else if (OPT_MATCH("-reverse") || OPT_MATCH("-r")) {
+ flags |= FLAG_REVERSE;
+ } else if (OPT_MATCH("-nolocalmap") || OPT_MATCH("-L")) {
+ flags &= ~FLAG_LOCALMAP;
+ } else if (OPT_MATCH("-nonameprep") || OPT_MATCH("-N")) {
+ flags &= ~FLAG_NAMEPREP;
+ } else if (OPT_MATCH("-unassigncheck") || OPT_MATCH("-u")) {
+ flags |= FLAG_UNASSIGNCHECK;
+ } else if (OPT_MATCH("-nounassigncheck") || OPT_MATCH("-U")) {
+ flags &= ~FLAG_UNASSIGNCHECK;
+ } else if (OPT_MATCH("-nobidicheck") || OPT_MATCH("-B")) {
+ flags &= ~FLAG_BIDICHECK;
+ } else if (OPT_MATCH("-noasciicheck") || OPT_MATCH("-A")) {
+ flags &= ~FLAG_ASCIICHECK;
+ } else if (OPT_MATCH("-nolengthcheck")) {
+ flags &= ~FLAG_LENGTHCHECK;
+ } else if (OPT_MATCH("-noroundtripcheck")) {
+ flags &= ~FLAG_ROUNDTRIPCHECK;
+ } else if (OPT_MATCH("-whole") || OPT_MATCH("-w")) {
+ flags &= ~FLAG_SELECTIVE;
+ } else if (OPT_MATCH("-localmap")) {
+ MUST_HAVE_ARG;
+ APPEND_LIST(localmappers, nlocalmappers, av[1],
+ "local maps");
+ } else if (OPT_MATCH("-delimiter")) {
+ unsigned long v;
+ MUST_HAVE_ARG;
+ v = get_ucs(av[1]);
+ APPEND_LIST(delimiters, ndelimiters, v,
+ "delimiter maps");
+ } else if (OPT_MATCH("-alias") || OPT_MATCH("-a")) {
+ MUST_HAVE_ARG;
+ encoding_alias = av[1];
+ ac--;
+ av++;
+ } else if (OPT_MATCH("-flush")) {
+ flush_every_line = 1;
+ } else if (OPT_MATCH("-version") || OPT_MATCH("-v")) {
+ print_version();
+ } else {
+ print_usage(cmd);
+ }
+#undef OPT_MATCH
+#undef MUST_HAVE_ARG
+#undef APPEND_LIST
+
+ ac--;
+ av++;
+ }
+
+ if (ac > 1)
+ print_usage(cmd);
+
+ /* Initialize. */
+ if ((r = idn_resconf_initialize()) != idn_success) {
+ errormsg("error initializing library\n");
+ return (1);
+ }
+
+ /*
+ * Create resource contexts.
+ * `resconf1' and `resconf2' are almost the same but local and
+ * IDN encodings are reversed.
+ */
+ resconf1 = NULL;
+ resconf2 = NULL;
+ if (idn_resconf_create(&resconf1) != idn_success ||
+ idn_resconf_create(&resconf2) != idn_success) {
+ errormsg("error initializing configuration contexts\n");
+ return (1);
+ }
+
+ /* Load configuration file. */
+ if (no_resconf) {
+ set_defaults(resconf1);
+ set_defaults(resconf2);
+ } else {
+ load_conf_file(resconf1, resconf_file);
+ load_conf_file(resconf2, resconf_file);
+ }
+
+ /* Set encoding alias file. */
+ if (encoding_alias != NULL)
+ set_encoding_alias(encoding_alias);
+
+ /* Set input codeset. */
+ if (flags & FLAG_REVERSE) {
+ if (in_code == NULL) {
+ conv = idn_resconf_getidnconverter(resconf1);
+ if (conv == NULL) {
+ errormsg("cannot get the IDN encoding.\n"
+ "please specify an appropriate one "
+ "with `-in' option.\n");
+ exit(1);
+ }
+ idn_resconf_setlocalconverter(resconf2, conv);
+ idn_converter_destroy(conv);
+ } else {
+ set_idncode(resconf1, in_code);
+ set_localcode(resconf2, in_code);
+ }
+ } else {
+ if (in_code == NULL) {
+ conv = idn_resconf_getlocalconverter(resconf1);
+ if (conv == NULL) {
+ errormsg("cannot get the local encoding.\n"
+ "please specify an appropriate one "
+ "with `-in' option.\n");
+ exit(1);
+ }
+ idn_resconf_setidnconverter(resconf2, conv);
+ idn_converter_destroy(conv);
+ } else {
+ set_localcode(resconf1, in_code);
+ set_idncode(resconf2, in_code);
+ }
+ }
+
+ /* Set output codeset. */
+ if (flags & FLAG_REVERSE) {
+ if (out_code == NULL) {
+ conv = idn_resconf_getlocalconverter(resconf1);
+ if (conv == NULL) {
+ errormsg("cannot get the local encoding.\n"
+ "please specify an appropriate one "
+ "with `-out' option.\n");
+ exit(1);
+ }
+ idn_resconf_setidnconverter(resconf2, conv);
+ idn_converter_destroy(conv);
+ } else {
+ set_localcode(resconf1, out_code);
+ set_idncode(resconf2, out_code);
+ }
+ } else {
+ if (out_code == NULL) {
+ conv = idn_resconf_getidnconverter(resconf1);
+ if (conv == NULL) {
+ errormsg("cannot get the IDN encoding.\n"
+ "please specify an appropriate one "
+ "with `-out' option.\n");
+ exit(1);
+ }
+ idn_resconf_setlocalconverter(resconf2, conv);
+ idn_converter_destroy(conv);
+ } else {
+ set_idncode(resconf1, out_code);
+ set_localcode(resconf2, out_code);
+ }
+ }
+
+ /* Set delimiter map(s). */
+ if (ndelimiters > 0) {
+ set_delimitermapper(resconf1, delimiters, ndelimiters);
+ set_delimitermapper(resconf2, delimiters, ndelimiters);
+ }
+
+ /* Set local map(s). */
+ if (nlocalmappers > 0) {
+ set_localmapper(resconf1, localmappers, nlocalmappers);
+ set_localmapper(resconf2, localmappers, nlocalmappers);
+ }
+
+ /* Set NAMEPREP version. */
+ if (nameprep_version != NULL) {
+ set_nameprep(resconf1, nameprep_version);
+ set_nameprep(resconf2, nameprep_version);
+ }
+
+ idn_res_enable(1);
+
+ /* Open input file. */
+ if (ac > 0) {
+ if ((fp = fopen(av[0], "r")) == NULL) {
+ errormsg("cannot open file %s: %s\n",
+ av[0], strerror(errno));
+ return (1);
+ }
+ } else {
+ fp = stdin;
+ }
+
+ /* Do the conversion. */
+ if (flags & FLAG_REVERSE)
+ exit_value = decode_file(resconf1, resconf2, fp, flags);
+ else
+ exit_value = encode_file(resconf1, resconf2, fp, flags);
+
+ idn_resconf_destroy(resconf1);
+ idn_resconf_destroy(resconf2);
+
+ return exit_value;
+}
+
+static int
+encode_file(idn_resconf_t conf1, idn_resconf_t conf2, FILE *fp, int flags) {
+ idn_result_t r;
+ idnconv_strbuf_t buf1, buf2;
+ idn_action_t actions1, actions2;
+ int nl_trimmed;
+ int local_ace_hack;
+ idn_converter_t conv;
+
+ /*
+ * See if the input codeset is an ACE.
+ */
+ conv = idn_resconf_getlocalconverter(conf1);
+ if (conv != NULL && idn_converter_isasciicompatible(conv) &&
+ (flags & FLAG_SELECTIVE))
+ local_ace_hack = 1;
+ else
+ local_ace_hack = 0;
+ if (conv != NULL)
+ idn_converter_destroy(conv);
+
+ if (local_ace_hack) {
+ actions1 = IDN_IDNCONV;
+ if (flags & FLAG_ROUNDTRIPCHECK)
+ actions1 |= IDN_RTCHECK;
+ } else {
+ actions1 = IDN_LOCALCONV;
+ }
+
+ actions2 = IDN_IDNCONV;
+ if (flags & FLAG_DELIMMAP)
+ actions2 |= IDN_DELIMMAP;
+ if (flags & FLAG_LOCALMAP)
+ actions2 |= IDN_LOCALMAP;
+ if (flags & FLAG_MAP)
+ actions2 |= IDN_MAP;
+ if (flags & FLAG_NORMALIZE)
+ actions2 |= IDN_NORMALIZE;
+ if (flags & FLAG_PROHIBITCHECK)
+ actions2 |= IDN_PROHCHECK;
+ if (flags & FLAG_UNASSIGNCHECK)
+ actions2 |= IDN_UNASCHECK;
+ if (flags & FLAG_BIDICHECK)
+ actions2 |= IDN_BIDICHECK;
+ if (flags & FLAG_ASCIICHECK)
+ actions2 |= IDN_ASCCHECK;
+ if (flags & FLAG_LENGTHCHECK)
+ actions2 |= IDN_LENCHECK;
+
+ strbuf_init(&buf1);
+ strbuf_init(&buf2);
+ line_number = 1;
+ while (strbuf_getline(&buf1, fp) != NULL) {
+ /*
+ * Trim newline at the end. This is needed for
+ * those ascii-comatible encodings such as UTF-5 or RACE
+ * not to try converting newlines, which will result
+ * in `invalid encoding' error.
+ */
+ nl_trimmed = trim_newline(&buf1);
+
+ /*
+ * Convert input line to UTF-8.
+ */
+ if (local_ace_hack)
+ r = convert_line(&buf1, &buf2, conf2, actions1,
+ FLAG_REVERSE|FLAG_SELECTIVE);
+ else
+ r = convert_line(&buf1, &buf2, conf1, actions1,
+ 0);
+
+ if (r != idn_success) {
+ errormsg("conversion failed at line %d: %s\n",
+ line_number,
+ idn_result_tostring(r));
+ goto error;
+ }
+ if (!idn_utf8_isvalidstring(strbuf_get(&buf2))) {
+ errormsg("conversion to utf-8 failed at line %d\n",
+ line_number);
+ goto error;
+ }
+
+ /*
+ * Perform local mapping and NAMEPREP, and convert to
+ * the output codeset.
+ */
+ r = convert_line(&buf2, &buf1, conf1, actions2,
+ flags & FLAG_SELECTIVE);
+
+ if (r != idn_success) {
+ errormsg("error in nameprep or output conversion "
+ "at line %d: %s\n",
+ line_number, idn_result_tostring(r));
+ goto error;
+ }
+
+ fputs(strbuf_get(&buf1), stdout);
+ if (nl_trimmed)
+ putc('\n', stdout);
+
+ if (flush_every_line)
+ fflush(stdout);
+
+ line_number++;
+ }
+
+ strbuf_reset(&buf1);
+ strbuf_reset(&buf2);
+ return (0);
+
+ error:
+ strbuf_reset(&buf1);
+ strbuf_reset(&buf2);
+ return (1);
+}
+
+static int
+decode_file(idn_resconf_t conf1, idn_resconf_t conf2, FILE *fp, int flags) {
+ idn_result_t r;
+ idnconv_strbuf_t buf1, buf2;
+ idn_action_t actions1, actions2;
+ int nl_trimmed;
+ int local_ace_hack, idn_ace_hack;
+ idn_converter_t conv;
+
+ /*
+ * See if the input codeset is an ACE.
+ */
+ conv = idn_resconf_getidnconverter(conf1);
+ if (conv != NULL && idn_converter_isasciicompatible(conv) &&
+ (flags & FLAG_SELECTIVE))
+ idn_ace_hack = 1;
+ else
+ idn_ace_hack = 0;
+ if (conv != NULL)
+ idn_converter_destroy(conv);
+
+ conv = idn_resconf_getlocalconverter(conf1);
+ if (conv != NULL && idn_converter_isasciicompatible(conv) &&
+ (flags & FLAG_SELECTIVE))
+ local_ace_hack = 1;
+ else
+ local_ace_hack = 0;
+ if (conv != NULL)
+ idn_converter_destroy(conv);
+
+ actions1 = IDN_IDNCONV;
+
+ if (local_ace_hack) {
+ actions2 = IDN_IDNCONV;
+ if (flags & FLAG_MAP)
+ actions2 |= IDN_MAP;
+ if (flags & FLAG_NORMALIZE)
+ actions2 |= IDN_NORMALIZE;
+ if (flags & FLAG_PROHIBITCHECK)
+ actions2 |= IDN_PROHCHECK;
+ if (flags & FLAG_UNASSIGNCHECK)
+ actions2 |= IDN_UNASCHECK;
+ if (flags & FLAG_BIDICHECK)
+ actions2 |= IDN_BIDICHECK;
+ if (flags & FLAG_ASCIICHECK)
+ actions2 |= IDN_ASCCHECK;
+ if (flags & FLAG_LENGTHCHECK)
+ actions2 |= IDN_LENCHECK;
+ } else {
+ actions2 = IDN_LOCALCONV;
+ }
+
+ if (flags & FLAG_DELIMMAP)
+ actions1 |= IDN_DELIMMAP;
+ if (flags & FLAG_MAP)
+ actions1 |= IDN_MAP;
+ if (flags & FLAG_NORMALIZE)
+ actions1 |= IDN_NORMALIZE;
+ if (flags & FLAG_NORMALIZE)
+ actions1 |= IDN_NORMALIZE;
+ if (flags & FLAG_PROHIBITCHECK)
+ actions1 |= IDN_PROHCHECK;
+ if (flags & FLAG_UNASSIGNCHECK)
+ actions1 |= IDN_UNASCHECK;
+ if (flags & FLAG_BIDICHECK)
+ actions1 |= IDN_BIDICHECK;
+ if (flags & FLAG_ASCIICHECK)
+ actions1 |= IDN_ASCCHECK;
+ if (flags & FLAG_ROUNDTRIPCHECK)
+ actions1 |= IDN_RTCHECK;
+
+ strbuf_init(&buf1);
+ strbuf_init(&buf2);
+ line_number = 1;
+ while (strbuf_getline(&buf1, fp) != NULL) {
+ /*
+ * Trim newline at the end. This is needed for
+ * those ascii-comatible encodings such as UTF-5 or RACE
+ * not to try converting newlines, which will result
+ * in `invalid encoding' error.
+ */
+ nl_trimmed = trim_newline(&buf1);
+
+ /*
+ * Treat input line as the string encoded in local
+ * encoding and convert it to UTF-8 encoded string.
+ */
+ if (local_ace_hack) {
+ if (strbuf_copy(&buf2, strbuf_get(&buf1)) == NULL)
+ r = idn_nomemory;
+ else
+ r = idn_success;
+ } else {
+ r = convert_line(&buf1, &buf2, conf1, IDN_LOCALCONV,
+ 0);
+ }
+ if (r != idn_success) {
+ errormsg("conversion failed at line %d: %s\n",
+ line_number, idn_result_tostring(r));
+ goto error;
+ }
+
+ /*
+ * Convert internationalized domain names in the line.
+ */
+ if (idn_ace_hack) {
+ r = convert_line(&buf2, &buf1, conf1, actions1,
+ FLAG_REVERSE|FLAG_SELECTIVE);
+ } else {
+ r = convert_line(&buf2, &buf1, conf1, actions1,
+ FLAG_REVERSE);
+ }
+ if (r != idn_success) {
+ errormsg("conversion failed at line %d: %s\n",
+ line_number,
+ idn_result_tostring(r));
+ goto error;
+ }
+ if (!idn_utf8_isvalidstring(strbuf_get(&buf1))) {
+ errormsg("conversion to utf-8 failed at line %d\n",
+ line_number);
+ goto error;
+ }
+
+ /*
+ * Perform round trip check and convert to the output
+ * codeset.
+ */
+ if (local_ace_hack) {
+ r = convert_line(&buf1, &buf2, conf2, actions2,
+ FLAG_SELECTIVE);
+ } else {
+ r = convert_line(&buf1, &buf2, conf1, actions2,
+ FLAG_REVERSE);
+ }
+
+ if (r != idn_success) {
+ errormsg("error in nameprep or output conversion "
+ "at line %d: %s\n",
+ line_number, idn_result_tostring(r));
+ goto error;
+ }
+
+ fputs(strbuf_get(&buf2), stdout);
+ if (nl_trimmed)
+ putc('\n', stdout);
+
+ if (flush_every_line)
+ fflush(stdout);
+
+ line_number++;
+ }
+ strbuf_reset(&buf1);
+ strbuf_reset(&buf2);
+ return (0);
+
+ error:
+ strbuf_reset(&buf1);
+ strbuf_reset(&buf2);
+ return (1);
+}
+
+static int
+trim_newline(idnconv_strbuf_t *buf) {
+ /*
+ * If the string in BUF ends with a newline, trim it and
+ * return 1. Otherwise, just return 0 without modifying BUF.
+ */
+ char *s = strbuf_get(buf);
+ size_t len = strlen(s);
+
+ if (s[len - 1] == '\n') {
+ s[len - 1] = '\0';
+ return (1);
+ }
+
+ return (0);
+}
+
+static idn_result_t
+convert_line(idnconv_strbuf_t *from, idnconv_strbuf_t *to,
+ idn_resconf_t conf, idn_action_t actions, int flags)
+{
+ idn_result_t r = idn_success;
+ char *from_str = strbuf_get(from);
+
+ for (;;) {
+ char *to_str = strbuf_get(to);
+ size_t to_size = strbuf_size(to);
+
+ switch (flags & (FLAG_REVERSE|FLAG_SELECTIVE)) {
+ case 0:
+ r = idn_res_encodename(conf, actions, from_str,
+ to_str, to_size);
+ break;
+ case FLAG_REVERSE:
+ r = idn_res_decodename(conf, actions, from_str,
+ to_str, to_size);
+ break;
+ case FLAG_SELECTIVE:
+ r = selective_encode(conf, actions, from_str,
+ to_str, to_size);
+ break;
+ case FLAG_REVERSE|FLAG_SELECTIVE:
+ r = selective_decode(conf, actions, from_str,
+ to_str, to_size);
+ break;
+ }
+ if (r == idn_buffer_overflow) {
+ /*
+ * Conversion is not successful because
+ * the size of the target buffer is not enough.
+ * Double the size and retry.
+ */
+ if (strbuf_double(to) == NULL) {
+ /* oops. allocation failed. */
+ return (idn_nomemory);
+ }
+ } else {
+ break;
+ }
+ }
+ return (r);
+}
+
+static char *options[] = {
+ "-in INPUT-CODESET : specifies input codeset name.",
+ "-i INPUT-CODESET : synonym for -in",
+ "-out OUTPUT-CODESET : specifies output codeset name.",
+ "-o OUTPUT-CODESET : synonym for -out",
+ "-conf CONF-FILE : specifies idnkit configuration file.",
+ "-c CONF-FILE : synonym for -conf",
+ "-noconf : do not load idnkit configuration file.",
+ "-C : synonym for -noconf",
+ "-reverse : specifies reverse conversion.",
+ " (i.e. IDN encoding to local encoding)",
+ "-r : synonym for -reverse",
+ "-nameprep VERSION : specifies version name of NAMEPREP.",
+ "-n VERSION : synonym for -nameprep",
+ "-nonameprep : do not perform NAMEPREP.",
+ "-N : synonym for -nonameprep",
+ "-localmap MAPPING : specifies local mapping.",
+ "-nolocalmap : do not perform local mapping.",
+ "-L : synonym for -nolocalmap",
+ "-nounassigncheck : do not perform unassigned codepoint check.",
+ "-U : synonym for -nounassigncheck",
+ "-nobidicheck : do not perform bidirectional text check.",
+ "-B : synonym for -nobidicheck",
+ "-nolengthcheck : do not check label length.",
+ "-noasciicheck : do not check ASCII range characters.",
+ "-A : synonym for -noasciicheck",
+ "-noroundtripcheck : do not perform round trip check.",
+ "-delimiter U+XXXX : specifies local delimiter code point.",
+ "-alias alias-file : specifies codeset alias file.",
+ "-a : synonym for -alias",
+ "-flush : line-buffering mode.",
+ "-whole : convert the whole region instead of",
+ " regions containing non-ascii characters.",
+ "-w : synonym for -whole",
+ "-version : print version number, then exit.",
+ "-v : synonym for -version",
+ "",
+ " The following options can be specified multiple times",
+ " -localmap, -delimiter",
+ NULL,
+};
+
+static void
+print_version() {
+ fprintf(stderr, "idnconv (idnkit) version: %s\n"
+ "library version: %s\n",
+ IDNKIT_VERSION,
+ idn_version_getstring());
+ exit(0);
+}
+
+static void
+print_usage(char *cmd) {
+ int i;
+
+ fprintf(stderr, "Usage: %s [options..] [file]\n", cmd);
+
+ for (i = 0; options[i] != NULL; i++)
+ fprintf(stderr, "\t%s\n", options[i]);
+
+ exit(1);
+}
+
+static unsigned long
+get_ucs(const char *p) {
+ unsigned long v;
+ char *end;
+
+ /* Skip optional 'U+' */
+ if (strncmp(p, "U+", 2) == 0)
+ p += 2;
+
+ v = strtoul(p, &end, 16);
+ if (*end != '\0') {
+ fprintf(stderr, "invalid UCS code point \"%s\"\n", p);
+ exit(1);
+ }
+
+ return v;
+}