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