1/* $NetBSD: idnconv.c,v 1.2 2011/02/16 03:46:57 christos Exp $ */ 2 3#ifndef lint 4static char *rcsid = "Id: idnconv.c,v 1.1 2003/06/04 00:27:07 marka Exp "; 5#endif 6 7/* 8 * Copyright (c) 2000,2001,2002 Japan Network Information Center. 9 * All rights reserved. 10 * 11 * By using this file, you agree to the terms and conditions set forth bellow. 12 * 13 * LICENSE TERMS AND CONDITIONS 14 * 15 * The following License Terms and Conditions apply, unless a different 16 * license is obtained from Japan Network Information Center ("JPNIC"), 17 * a Japanese association, Kokusai-Kougyou-Kanda Bldg 6F, 2-3-4 Uchi-Kanda, 18 * Chiyoda-ku, Tokyo 101-0047, Japan. 19 * 20 * 1. Use, Modification and Redistribution (including distribution of any 21 * modified or derived work) in source and/or binary forms is permitted 22 * under this License Terms and Conditions. 23 * 24 * 2. Redistribution of source code must retain the copyright notices as they 25 * appear in each source code file, this License Terms and Conditions. 26 * 27 * 3. Redistribution in binary form must reproduce the Copyright Notice, 28 * this License Terms and Conditions, in the documentation and/or other 29 * materials provided with the distribution. For the purposes of binary 30 * distribution the "Copyright Notice" refers to the following language: 31 * "Copyright (c) 2000-2002 Japan Network Information Center. All rights reserved." 32 * 33 * 4. The name of JPNIC may not be used to endorse or promote products 34 * derived from this Software without specific prior written approval of 35 * JPNIC. 36 * 37 * 5. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY JPNIC 38 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 39 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 40 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JPNIC BE LIABLE 41 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 42 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 43 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 44 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 45 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 46 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 47 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 48 */ 49 50/* 51 * idnconv -- Codeset converter for named.conf and zone files 52 */ 53 54#include <config.h> 55 56#include <stdio.h> 57#include <stddef.h> 58#include <stdlib.h> 59#include <string.h> 60#include <errno.h> 61#ifdef HAVE_LOCALE_H 62#include <locale.h> 63#endif 64 65#include <idn/result.h> 66#include <idn/converter.h> 67#include <idn/normalizer.h> 68#include <idn/utf8.h> 69#include <idn/resconf.h> 70#include <idn/res.h> 71#include <idn/util.h> 72#include <idn/version.h> 73 74#include "util.h" 75 76#define MAX_DELIMITER 10 77#define MAX_LOCALMAPPER 10 78#define MAX_MAPPER 10 79#define MAX_NORMALIZER 10 80#define MAX_CHEKER 10 81 82#define FLAG_REVERSE 0x0001 83#define FLAG_DELIMMAP 0x0002 84#define FLAG_LOCALMAP 0x0004 85#define FLAG_MAP 0x0008 86#define FLAG_NORMALIZE 0x0010 87#define FLAG_PROHIBITCHECK 0x0020 88#define FLAG_UNASSIGNCHECK 0x0040 89#define FLAG_BIDICHECK 0x0080 90#define FLAG_ASCIICHECK 0x0100 91#define FLAG_LENGTHCHECK 0x0200 92#define FLAG_ROUNDTRIPCHECK 0x0400 93#define FLAG_SELECTIVE 0x0800 94 95#define FLAG_NAMEPREP \ 96 (FLAG_MAP|FLAG_NORMALIZE|FLAG_PROHIBITCHECK|FLAG_UNASSIGNCHECK|\ 97 FLAG_BIDICHECK) 98 99#define DEFAULT_FLAGS \ 100 (FLAG_LOCALMAP|FLAG_NAMEPREP|FLAG_ASCIICHECK|FLAG_LENGTHCHECK|\ 101 FLAG_ROUNDTRIPCHECK|FLAG_SELECTIVE|FLAG_DELIMMAP) 102 103int line_number; /* current input file line number */ 104static int flush_every_line = 0; /* pretty obvious */ 105 106static int encode_file(idn_resconf_t conf1, idn_resconf_t conf2, 107 FILE *fp, int flags); 108static int decode_file(idn_resconf_t conf1, idn_resconf_t conf2, 109 FILE *fp, int flags); 110static int trim_newline(idnconv_strbuf_t *buf); 111static idn_result_t convert_line(idnconv_strbuf_t *from, 112 idnconv_strbuf_t *to, 113 idn_resconf_t conf, 114 idn_action_t actions, int flags); 115static void print_usage(char *cmd); 116static void print_version(void); 117static unsigned long get_ucs(const char *p); 118 119int 120main(int ac, char **av) { 121 char *cmd = *av; 122 char *cname; 123 unsigned long delimiters[MAX_DELIMITER]; 124 char *localmappers[MAX_LOCALMAPPER]; 125 char *nameprep_version = NULL; 126 int ndelimiters = 0; 127 int nlocalmappers = 0; 128 char *in_code = NULL; 129 char *out_code = NULL; 130 char *resconf_file = NULL; 131 int no_resconf = 0; 132 char *encoding_alias = NULL; 133 int flags = DEFAULT_FLAGS; 134 FILE *fp; 135 idn_result_t r; 136 idn_resconf_t resconf1, resconf2; 137 idn_converter_t conv; 138 int exit_value; 139 140#ifdef HAVE_SETLOCALE 141 (void)setlocale(LC_ALL, ""); 142#endif 143 144 /* 145 * If the command name begins with 'r', reverse mode is assumed. 146 */ 147 if ((cname = strrchr(cmd, '/')) != NULL) 148 cname++; 149 else 150 cname = cmd; 151 if (cname[0] == 'r') 152 flags |= FLAG_REVERSE; 153 154 ac--; 155 av++; 156 while (ac > 0 && **av == '-') { 157 158#define OPT_MATCH(opt) (strcmp(*av, opt) == 0) 159#define MUST_HAVE_ARG if (ac < 2) print_usage(cmd) 160#define APPEND_LIST(array, size, item, what) \ 161 if (size >= (sizeof(array) / sizeof(array[0]))) { \ 162 errormsg("too many " what "\n"); \ 163 exit(1); \ 164 } \ 165 array[size++] = item; \ 166 ac--; av++ 167 168 if (OPT_MATCH("-in") || OPT_MATCH("-i")) { 169 MUST_HAVE_ARG; 170 in_code = av[1]; 171 ac--; 172 av++; 173 } else if (OPT_MATCH("-out") || OPT_MATCH("-o")) { 174 MUST_HAVE_ARG; 175 out_code = av[1]; 176 ac--; 177 av++; 178 } else if (OPT_MATCH("-conf") || OPT_MATCH("-c")) { 179 MUST_HAVE_ARG; 180 resconf_file = av[1]; 181 ac--; 182 av++; 183 } else if (OPT_MATCH("-nameprep") || OPT_MATCH("-n")) { 184 MUST_HAVE_ARG; 185 nameprep_version = av[1]; 186 ac--; 187 av++; 188 } else if (OPT_MATCH("-noconf") || OPT_MATCH("-C")) { 189 no_resconf = 1; 190 } else if (OPT_MATCH("-reverse") || OPT_MATCH("-r")) { 191 flags |= FLAG_REVERSE; 192 } else if (OPT_MATCH("-nolocalmap") || OPT_MATCH("-L")) { 193 flags &= ~FLAG_LOCALMAP; 194 } else if (OPT_MATCH("-nonameprep") || OPT_MATCH("-N")) { 195 flags &= ~FLAG_NAMEPREP; 196 } else if (OPT_MATCH("-unassigncheck") || OPT_MATCH("-u")) { 197 flags |= FLAG_UNASSIGNCHECK; 198 } else if (OPT_MATCH("-nounassigncheck") || OPT_MATCH("-U")) { 199 flags &= ~FLAG_UNASSIGNCHECK; 200 } else if (OPT_MATCH("-nobidicheck") || OPT_MATCH("-B")) { 201 flags &= ~FLAG_BIDICHECK; 202 } else if (OPT_MATCH("-noasciicheck") || OPT_MATCH("-A")) { 203 flags &= ~FLAG_ASCIICHECK; 204 } else if (OPT_MATCH("-nolengthcheck")) { 205 flags &= ~FLAG_LENGTHCHECK; 206 } else if (OPT_MATCH("-noroundtripcheck")) { 207 flags &= ~FLAG_ROUNDTRIPCHECK; 208 } else if (OPT_MATCH("-whole") || OPT_MATCH("-w")) { 209 flags &= ~FLAG_SELECTIVE; 210 } else if (OPT_MATCH("-localmap")) { 211 MUST_HAVE_ARG; 212 APPEND_LIST(localmappers, nlocalmappers, av[1], 213 "local maps"); 214 } else if (OPT_MATCH("-delimiter")) { 215 unsigned long v; 216 MUST_HAVE_ARG; 217 v = get_ucs(av[1]); 218 APPEND_LIST(delimiters, ndelimiters, v, 219 "delimiter maps"); 220 } else if (OPT_MATCH("-alias") || OPT_MATCH("-a")) { 221 MUST_HAVE_ARG; 222 encoding_alias = av[1]; 223 ac--; 224 av++; 225 } else if (OPT_MATCH("-flush")) { 226 flush_every_line = 1; 227 } else if (OPT_MATCH("-version") || OPT_MATCH("-v")) { 228 print_version(); 229 } else { 230 print_usage(cmd); 231 } 232#undef OPT_MATCH 233#undef MUST_HAVE_ARG 234#undef APPEND_LIST 235 236 ac--; 237 av++; 238 } 239 240 if (ac > 1) 241 print_usage(cmd); 242 243 /* Initialize. */ 244 if ((r = idn_resconf_initialize()) != idn_success) { 245 errormsg("error initializing library\n"); 246 return (1); 247 } 248 249 /* 250 * Create resource contexts. 251 * `resconf1' and `resconf2' are almost the same but local and 252 * IDN encodings are reversed. 253 */ 254 resconf1 = NULL; 255 resconf2 = NULL; 256 if (idn_resconf_create(&resconf1) != idn_success || 257 idn_resconf_create(&resconf2) != idn_success) { 258 errormsg("error initializing configuration contexts\n"); 259 return (1); 260 } 261 262 /* Load configuration file. */ 263 if (no_resconf) { 264 set_defaults(resconf1); 265 set_defaults(resconf2); 266 } else { 267 load_conf_file(resconf1, resconf_file); 268 load_conf_file(resconf2, resconf_file); 269 } 270 271 /* Set encoding alias file. */ 272 if (encoding_alias != NULL) 273 set_encoding_alias(encoding_alias); 274 275 /* Set input codeset. */ 276 if (flags & FLAG_REVERSE) { 277 if (in_code == NULL) { 278 conv = idn_resconf_getidnconverter(resconf1); 279 if (conv == NULL) { 280 errormsg("cannot get the IDN encoding.\n" 281 "please specify an appropriate one " 282 "with `-in' option.\n"); 283 exit(1); 284 } 285 idn_resconf_setlocalconverter(resconf2, conv); 286 idn_converter_destroy(conv); 287 } else { 288 set_idncode(resconf1, in_code); 289 set_localcode(resconf2, in_code); 290 } 291 } else { 292 if (in_code == NULL) { 293 conv = idn_resconf_getlocalconverter(resconf1); 294 if (conv == NULL) { 295 errormsg("cannot get the local encoding.\n" 296 "please specify an appropriate one " 297 "with `-in' option.\n"); 298 exit(1); 299 } 300 idn_resconf_setidnconverter(resconf2, conv); 301 idn_converter_destroy(conv); 302 } else { 303 set_localcode(resconf1, in_code); 304 set_idncode(resconf2, in_code); 305 } 306 } 307 308 /* Set output codeset. */ 309 if (flags & FLAG_REVERSE) { 310 if (out_code == NULL) { 311 conv = idn_resconf_getlocalconverter(resconf1); 312 if (conv == NULL) { 313 errormsg("cannot get the local encoding.\n" 314 "please specify an appropriate one " 315 "with `-out' option.\n"); 316 exit(1); 317 } 318 idn_resconf_setidnconverter(resconf2, conv); 319 idn_converter_destroy(conv); 320 } else { 321 set_localcode(resconf1, out_code); 322 set_idncode(resconf2, out_code); 323 } 324 } else { 325 if (out_code == NULL) { 326 conv = idn_resconf_getidnconverter(resconf1); 327 if (conv == NULL) { 328 errormsg("cannot get the IDN encoding.\n" 329 "please specify an appropriate one " 330 "with `-out' option.\n"); 331 exit(1); 332 } 333 idn_resconf_setlocalconverter(resconf2, conv); 334 idn_converter_destroy(conv); 335 } else { 336 set_idncode(resconf1, out_code); 337 set_localcode(resconf2, out_code); 338 } 339 } 340 341 /* Set delimiter map(s). */ 342 if (ndelimiters > 0) { 343 set_delimitermapper(resconf1, delimiters, ndelimiters); 344 set_delimitermapper(resconf2, delimiters, ndelimiters); 345 } 346 347 /* Set local map(s). */ 348 if (nlocalmappers > 0) { 349 set_localmapper(resconf1, localmappers, nlocalmappers); 350 set_localmapper(resconf2, localmappers, nlocalmappers); 351 } 352 353 /* Set NAMEPREP version. */ 354 if (nameprep_version != NULL) { 355 set_nameprep(resconf1, nameprep_version); 356 set_nameprep(resconf2, nameprep_version); 357 } 358 359 idn_res_enable(1); 360 361 /* Open input file. */ 362 if (ac > 0) { 363 if ((fp = fopen(av[0], "r")) == NULL) { 364 errormsg("cannot open file %s: %s\n", 365 av[0], strerror(errno)); 366 return (1); 367 } 368 } else { 369 fp = stdin; 370 } 371 372 /* Do the conversion. */ 373 if (flags & FLAG_REVERSE) 374 exit_value = decode_file(resconf1, resconf2, fp, flags); 375 else 376 exit_value = encode_file(resconf1, resconf2, fp, flags); 377 378 idn_resconf_destroy(resconf1); 379 idn_resconf_destroy(resconf2); 380 381 return exit_value; 382} 383 384static int 385encode_file(idn_resconf_t conf1, idn_resconf_t conf2, FILE *fp, int flags) { 386 idn_result_t r; 387 idnconv_strbuf_t buf1, buf2; 388 idn_action_t actions1, actions2; 389 int nl_trimmed; 390 int local_ace_hack; 391 idn_converter_t conv; 392 393 /* 394 * See if the input codeset is an ACE. 395 */ 396 conv = idn_resconf_getlocalconverter(conf1); 397 if (conv != NULL && idn_converter_isasciicompatible(conv) && 398 (flags & FLAG_SELECTIVE)) 399 local_ace_hack = 1; 400 else 401 local_ace_hack = 0; 402 if (conv != NULL) 403 idn_converter_destroy(conv); 404 405 if (local_ace_hack) { 406 actions1 = IDN_IDNCONV; 407 if (flags & FLAG_ROUNDTRIPCHECK) 408 actions1 |= IDN_RTCHECK; 409 } else { 410 actions1 = IDN_LOCALCONV; 411 } 412 413 actions2 = IDN_IDNCONV; 414 if (flags & FLAG_DELIMMAP) 415 actions2 |= IDN_DELIMMAP; 416 if (flags & FLAG_LOCALMAP) 417 actions2 |= IDN_LOCALMAP; 418 if (flags & FLAG_MAP) 419 actions2 |= IDN_MAP; 420 if (flags & FLAG_NORMALIZE) 421 actions2 |= IDN_NORMALIZE; 422 if (flags & FLAG_PROHIBITCHECK) 423 actions2 |= IDN_PROHCHECK; 424 if (flags & FLAG_UNASSIGNCHECK) 425 actions2 |= IDN_UNASCHECK; 426 if (flags & FLAG_BIDICHECK) 427 actions2 |= IDN_BIDICHECK; 428 if (flags & FLAG_ASCIICHECK) 429 actions2 |= IDN_ASCCHECK; 430 if (flags & FLAG_LENGTHCHECK) 431 actions2 |= IDN_LENCHECK; 432 433 strbuf_init(&buf1); 434 strbuf_init(&buf2); 435 line_number = 1; 436 while (strbuf_getline(&buf1, fp) != NULL) { 437 /* 438 * Trim newline at the end. This is needed for 439 * those ascii-comatible encodings such as UTF-5 or RACE 440 * not to try converting newlines, which will result 441 * in `invalid encoding' error. 442 */ 443 nl_trimmed = trim_newline(&buf1); 444 445 /* 446 * Convert input line to UTF-8. 447 */ 448 if (local_ace_hack) 449 r = convert_line(&buf1, &buf2, conf2, actions1, 450 FLAG_REVERSE|FLAG_SELECTIVE); 451 else 452 r = convert_line(&buf1, &buf2, conf1, actions1, 453 0); 454 455 if (r != idn_success) { 456 errormsg("conversion failed at line %d: %s\n", 457 line_number, 458 idn_result_tostring(r)); 459 goto error; 460 } 461 if (!idn_utf8_isvalidstring(strbuf_get(&buf2))) { 462 errormsg("conversion to utf-8 failed at line %d\n", 463 line_number); 464 goto error; 465 } 466 467 /* 468 * Perform local mapping and NAMEPREP, and convert to 469 * the output codeset. 470 */ 471 r = convert_line(&buf2, &buf1, conf1, actions2, 472 flags & FLAG_SELECTIVE); 473 474 if (r != idn_success) { 475 errormsg("error in nameprep or output conversion " 476 "at line %d: %s\n", 477 line_number, idn_result_tostring(r)); 478 goto error; 479 } 480 481 fputs(strbuf_get(&buf1), stdout); 482 if (nl_trimmed) 483 putc('\n', stdout); 484 485 if (flush_every_line) 486 fflush(stdout); 487 488 line_number++; 489 } 490 491 strbuf_reset(&buf1); 492 strbuf_reset(&buf2); 493 return (0); 494 495 error: 496 strbuf_reset(&buf1); 497 strbuf_reset(&buf2); 498 return (1); 499} 500 501static int 502decode_file(idn_resconf_t conf1, idn_resconf_t conf2, FILE *fp, int flags) { 503 idn_result_t r; 504 idnconv_strbuf_t buf1, buf2; 505 idn_action_t actions1, actions2; 506 int nl_trimmed; 507 int local_ace_hack, idn_ace_hack; 508 idn_converter_t conv; 509 510 /* 511 * See if the input codeset is an ACE. 512 */ 513 conv = idn_resconf_getidnconverter(conf1); 514 if (conv != NULL && idn_converter_isasciicompatible(conv) && 515 (flags & FLAG_SELECTIVE)) 516 idn_ace_hack = 1; 517 else 518 idn_ace_hack = 0; 519 if (conv != NULL) 520 idn_converter_destroy(conv); 521 522 conv = idn_resconf_getlocalconverter(conf1); 523 if (conv != NULL && idn_converter_isasciicompatible(conv) && 524 (flags & FLAG_SELECTIVE)) 525 local_ace_hack = 1; 526 else 527 local_ace_hack = 0; 528 if (conv != NULL) 529 idn_converter_destroy(conv); 530 531 actions1 = IDN_IDNCONV; 532 533 if (local_ace_hack) { 534 actions2 = IDN_IDNCONV; 535 if (flags & FLAG_MAP) 536 actions2 |= IDN_MAP; 537 if (flags & FLAG_NORMALIZE) 538 actions2 |= IDN_NORMALIZE; 539 if (flags & FLAG_PROHIBITCHECK) 540 actions2 |= IDN_PROHCHECK; 541 if (flags & FLAG_UNASSIGNCHECK) 542 actions2 |= IDN_UNASCHECK; 543 if (flags & FLAG_BIDICHECK) 544 actions2 |= IDN_BIDICHECK; 545 if (flags & FLAG_ASCIICHECK) 546 actions2 |= IDN_ASCCHECK; 547 if (flags & FLAG_LENGTHCHECK) 548 actions2 |= IDN_LENCHECK; 549 } else { 550 actions2 = IDN_LOCALCONV; 551 } 552 553 if (flags & FLAG_DELIMMAP) 554 actions1 |= IDN_DELIMMAP; 555 if (flags & FLAG_MAP) 556 actions1 |= IDN_MAP; 557 if (flags & FLAG_NORMALIZE) 558 actions1 |= IDN_NORMALIZE; 559 if (flags & FLAG_NORMALIZE) 560 actions1 |= IDN_NORMALIZE; 561 if (flags & FLAG_PROHIBITCHECK) 562 actions1 |= IDN_PROHCHECK; 563 if (flags & FLAG_UNASSIGNCHECK) 564 actions1 |= IDN_UNASCHECK; 565 if (flags & FLAG_BIDICHECK) 566 actions1 |= IDN_BIDICHECK; 567 if (flags & FLAG_ASCIICHECK) 568 actions1 |= IDN_ASCCHECK; 569 if (flags & FLAG_ROUNDTRIPCHECK) 570 actions1 |= IDN_RTCHECK; 571 572 strbuf_init(&buf1); 573 strbuf_init(&buf2); 574 line_number = 1; 575 while (strbuf_getline(&buf1, fp) != NULL) { 576 /* 577 * Trim newline at the end. This is needed for 578 * those ascii-comatible encodings such as UTF-5 or RACE 579 * not to try converting newlines, which will result 580 * in `invalid encoding' error. 581 */ 582 nl_trimmed = trim_newline(&buf1); 583 584 /* 585 * Treat input line as the string encoded in local 586 * encoding and convert it to UTF-8 encoded string. 587 */ 588 if (local_ace_hack) { 589 if (strbuf_copy(&buf2, strbuf_get(&buf1)) == NULL) 590 r = idn_nomemory; 591 else 592 r = idn_success; 593 } else { 594 r = convert_line(&buf1, &buf2, conf1, IDN_LOCALCONV, 595 0); 596 } 597 if (r != idn_success) { 598 errormsg("conversion failed at line %d: %s\n", 599 line_number, idn_result_tostring(r)); 600 goto error; 601 } 602 603 /* 604 * Convert internationalized domain names in the line. 605 */ 606 if (idn_ace_hack) { 607 r = convert_line(&buf2, &buf1, conf1, actions1, 608 FLAG_REVERSE|FLAG_SELECTIVE); 609 } else { 610 r = convert_line(&buf2, &buf1, conf1, actions1, 611 FLAG_REVERSE); 612 } 613 if (r != idn_success) { 614 errormsg("conversion failed at line %d: %s\n", 615 line_number, 616 idn_result_tostring(r)); 617 goto error; 618 } 619 if (!idn_utf8_isvalidstring(strbuf_get(&buf1))) { 620 errormsg("conversion to utf-8 failed at line %d\n", 621 line_number); 622 goto error; 623 } 624 625 /* 626 * Perform round trip check and convert to the output 627 * codeset. 628 */ 629 if (local_ace_hack) { 630 r = convert_line(&buf1, &buf2, conf2, actions2, 631 FLAG_SELECTIVE); 632 } else { 633 r = convert_line(&buf1, &buf2, conf1, actions2, 634 FLAG_REVERSE); 635 } 636 637 if (r != idn_success) { 638 errormsg("error in nameprep or output conversion " 639 "at line %d: %s\n", 640 line_number, idn_result_tostring(r)); 641 goto error; 642 } 643 644 fputs(strbuf_get(&buf2), stdout); 645 if (nl_trimmed) 646 putc('\n', stdout); 647 648 if (flush_every_line) 649 fflush(stdout); 650 651 line_number++; 652 } 653 strbuf_reset(&buf1); 654 strbuf_reset(&buf2); 655 return (0); 656 657 error: 658 strbuf_reset(&buf1); 659 strbuf_reset(&buf2); 660 return (1); 661} 662 663static int 664trim_newline(idnconv_strbuf_t *buf) { 665 /* 666 * If the string in BUF ends with a newline, trim it and 667 * return 1. Otherwise, just return 0 without modifying BUF. 668 */ 669 char *s = strbuf_get(buf); 670 size_t len = strlen(s); 671 672 if (s[len - 1] == '\n') { 673 s[len - 1] = '\0'; 674 return (1); 675 } 676 677 return (0); 678} 679 680static idn_result_t 681convert_line(idnconv_strbuf_t *from, idnconv_strbuf_t *to, 682 idn_resconf_t conf, idn_action_t actions, int flags) 683{ 684 idn_result_t r = idn_success; 685 char *from_str = strbuf_get(from); 686 687 for (;;) { 688 char *to_str = strbuf_get(to); 689 size_t to_size = strbuf_size(to); 690 691 switch (flags & (FLAG_REVERSE|FLAG_SELECTIVE)) { 692 case 0: 693 r = idn_res_encodename(conf, actions, from_str, 694 to_str, to_size); 695 break; 696 case FLAG_REVERSE: 697 r = idn_res_decodename(conf, actions, from_str, 698 to_str, to_size); 699 break; 700 case FLAG_SELECTIVE: 701 r = selective_encode(conf, actions, from_str, 702 to_str, to_size); 703 break; 704 case FLAG_REVERSE|FLAG_SELECTIVE: 705 r = selective_decode(conf, actions, from_str, 706 to_str, to_size); 707 break; 708 } 709 if (r == idn_buffer_overflow) { 710 /* 711 * Conversion is not successful because 712 * the size of the target buffer is not enough. 713 * Double the size and retry. 714 */ 715 if (strbuf_double(to) == NULL) { 716 /* oops. allocation failed. */ 717 return (idn_nomemory); 718 } 719 } else { 720 break; 721 } 722 } 723 return (r); 724} 725 726static char *options[] = { 727 "-in INPUT-CODESET : specifies input codeset name.", 728 "-i INPUT-CODESET : synonym for -in", 729 "-out OUTPUT-CODESET : specifies output codeset name.", 730 "-o OUTPUT-CODESET : synonym for -out", 731 "-conf CONF-FILE : specifies idnkit configuration file.", 732 "-c CONF-FILE : synonym for -conf", 733 "-noconf : do not load idnkit configuration file.", 734 "-C : synonym for -noconf", 735 "-reverse : specifies reverse conversion.", 736 " (i.e. IDN encoding to local encoding)", 737 "-r : synonym for -reverse", 738 "-nameprep VERSION : specifies version name of NAMEPREP.", 739 "-n VERSION : synonym for -nameprep", 740 "-nonameprep : do not perform NAMEPREP.", 741 "-N : synonym for -nonameprep", 742 "-localmap MAPPING : specifies local mapping.", 743 "-nolocalmap : do not perform local mapping.", 744 "-L : synonym for -nolocalmap", 745 "-nounassigncheck : do not perform unassigned codepoint check.", 746 "-U : synonym for -nounassigncheck", 747 "-nobidicheck : do not perform bidirectional text check.", 748 "-B : synonym for -nobidicheck", 749 "-nolengthcheck : do not check label length.", 750 "-noasciicheck : do not check ASCII range characters.", 751 "-A : synonym for -noasciicheck", 752 "-noroundtripcheck : do not perform round trip check.", 753 "-delimiter U+XXXX : specifies local delimiter code point.", 754 "-alias alias-file : specifies codeset alias file.", 755 "-a : synonym for -alias", 756 "-flush : line-buffering mode.", 757 "-whole : convert the whole region instead of", 758 " regions containing non-ascii characters.", 759 "-w : synonym for -whole", 760 "-version : print version number, then exit.", 761 "-v : synonym for -version", 762 "", 763 " The following options can be specified multiple times", 764 " -localmap, -delimiter", 765 NULL, 766}; 767 768static void 769print_version() { 770 fprintf(stderr, "idnconv (idnkit) version: %s\n" 771 "library version: %s\n", 772 IDNKIT_VERSION, 773 idn_version_getstring()); 774 exit(0); 775} 776 777static void 778print_usage(char *cmd) { 779 int i; 780 781 fprintf(stderr, "Usage: %s [options..] [file]\n", cmd); 782 783 for (i = 0; options[i] != NULL; i++) 784 fprintf(stderr, "\t%s\n", options[i]); 785 786 exit(1); 787} 788 789static unsigned long 790get_ucs(const char *p) { 791 unsigned long v; 792 char *end; 793 794 /* Skip optional 'U+' */ 795 if (strncmp(p, "U+", 2) == 0) 796 p += 2; 797 798 v = strtoul(p, &end, 16); 799 if (*end != '\0') { 800 fprintf(stderr, "invalid UCS code point \"%s\"\n", p); 801 exit(1); 802 } 803 804 return v; 805} 806