1 /*
2 * TERMS AND CONDITIONS
3 * FOR
4 * OPEN SOURCE CODE LICENSE
5 * Version 1.1
6 *
7 * Japan Registry Services Co., Ltd. ("JPRS"), a Japanese corporation
8 * having its head office at Chiyoda First Bldg. East 13F 3-8-1 Nishi-Kanda,
9 * Chiyoda-ku, Tokyo 101-0065, Japan, grants you the license for open source
10 * code specified in EXHIBIT A the "Code" subject to the following Terms and
11 * Conditions ("OSCL").
12 *
13 * 1. License Grant.
14 * JPRS hereby grants you a worldwide, royalty-free, non-exclusive
15 * license, subject to third party intellectual property claims:
16 * (a) under intellectual property rights (other than patent or
17 * trademark) licensable by JPRS to use, reproduce, modify, display,
18 * perform, sublicense and distribute the Code (or portions thereof)
19 * with or without modifications, and/or as part of a derivative work;
20 * or
21 * (b) under claims of the infringement through the making, using,
22 * offering to sell and/or otherwise disposing the JPRS Revised Code
23 * (or portions thereof);
24 * (c) the licenses granted in this Section 1(a) and (b) are effective on
25 * the date JPRS first distributes the Code to you under the terms of
26 * this OSCL;
27 * (d) Notwithstanding the above stated terms, no patent license is
28 * granted:
29 * 1) for a code that you delete from the Code;
30 * 2) separate from the Code; or
31 * 3) for infringements caused by:
32 * i) modification of the Code; or
33 * ii) combination of the Code with other software or devices.
34 *
35 * 2. Consents.
36 * You agree that:
37 * (a) you must include a copy of this OSCL and the notice set forth in
38 * EXHIBIT A with every copy of the Code you distribute;
39 * (b) you must include a copy of this OSCL and the notice set forth in
40 * EXHIBIT A with every copy of binary form of the Code in the
41 * documentation and/or other materials provided with the distribution;
42 * (c) you may not offer or impose any terms on any source code version
43 * that alters or restricts the applicable version of this OSCL or
44 * the recipients' rights hereunder.
45 * (d) If the terms and conditions are set forth in EXHIBIT A, you must
46 * comply with those terms and conditions.
47 *
48 * 3. Proprietary Information.
49 * All trademarks, service marks, patents, copyrights, trade secrets, and
50 * other proprietary rights in or related to the Code are and will remain
51 * the exclusive property of JPRS or its licensors, whether or not
52 * specifically recognized or perfected under local law except specified
53 * in this OSCL; provided however you agree and understand that the JPRS
54 * name may not be used to endorse or promote this Code without prior
55 * written approval of JPRS.
56 *
57 * 4. WARRANTY DISCLAIMER.
58 * JPRS MAKES NO REPRESENTATIONS AND WARRANTIES REGARDING THE USE OF THE
59 * CODE, NOR DOES JPRS MAKE ANY REPRESENTATIONS THAT THE CODE WILL BECOME
60 * COMMERCIALLY AVAILABLE. JPRS, ITS AFFILIATES, AND ITS SUPPLIERS DO NOT
61 * WARRANT OR REPRESENT THAT THE CODE IS FREE OF ERRORS OR THAT THE CODE
62 * IS SUITABLE FOR TRANSLATION AND/OR LOCALIZATION. THE CODE IS PROVIDED
63 * ON AN "AS IS" BASIS AND JPRS AND ITS SUPPLIERS HAVE NO OBLIGATION TO
64 * CORRECT ERRORS OR TO SUPPORT THE CODE UNDER THIS OSCL FOR ANY REASON.
65 * TO THE FULL EXTENT PERMITTED BY LAW, ALL OBLIGATIONS ARE HEREBY
66 * EXCLUDED WHETHER EXPRESS, STATUTORY OR IMPLIED UNDER LAW, COURSE OF
67 * DEALING, CUSTOM, TRADE USAGE, ORAL OR WRITTEN STATEMENT OR OTHERWISE,
68 * INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OF MERCHANTABILITY
69 * OR FITNESS FOR A PARTICULAR PURPOSE CONCERNING THE CODE.
70 *
71 * 5. NO LIABILITY.
72 * UNDER NO CIRCUMSTANCES SHALL JPRS AND/OR ITS AFFILIATES, LICENSORS, OR
73 * REPRESENTATIVES BE LIABLE FOR ANY DAMAGES INCLUDING BUT NOT LIMITED TO
74 * CONSEQUENTIAL, INDIRECT, SPECIAL, PUNITIVE OR INCIDENTAL DAMAGES,
75 * WHETHER FORESEEABLE OR UNFORESEEABLE, BASED ON YOUR CLAIMS, INCLUDING,
76 * BUT NOT LIMITED TO, CLAIMS FOR LOSS OF DATA, GOODWILL, PROFITS, USE OF
77 * MONEY, INTERRUPTION IN USE OR AVAILABILITY OF DATA, STOPPAGE, IMPLIED
78 * WARRANTY, BREACH OF CONTRACT, MISREPRESENTATION, NEGLIGENCE, STRICT
79 * LIABILITY IN TORT, OR OTHERWISE.
80 *
81 * 6. Indemnification.
82 * You hereby agree to indemnify, defend, and hold harmless JPRS for any
83 * liability incurred by JRPS due to your terms of warranty, support,
84 * indemnity, or liability offered by you to any third party.
85 *
86 * 7. Termination.
87 * 7.1 This OSCL shall be automatically terminated in the events that:
88 * (a) You fail to comply with the terms herein and fail to cure such
89 * breach within 30 days of becoming aware of the breach;
90 * (b) You initiate patent or copyright infringement litigation against
91 * any party (including a cross-claim or counterclaim in a lawsuit)
92 * alleging that the Code constitutes a direct or indirect patent or
93 * copyright infringement, in such case, this OSCL to you shall
94 * terminate as of the date such litigation is filed;
95 * 7.2 In the event of termination under Sections 7.1(a) or 7.1(b) above,
96 * all end user license agreements (excluding distributors and
97 * resellers) which have been validly granted by You or any distributor
98 * hereunder prior to termination shall survive termination.
99 *
100 *
101 * 8. General.
102 * This OSCL shall be governed by, and construed and enforced in
103 * accordance with, the laws of Japan. Any litigation or arbitration
104 * between the parties shall be conducted exclusively in Tokyo, Japan
105 * except written consent of JPRS provides other venue.
106 *
107 *
108 * EXHIBIT A
109 *
110 * The original open source code of idnkit-2 is idnkit-1.0 developed and
111 * conceived by Japan Network Information Center ("JPNIC"), a Japanese
112 * association, Kokusai-Kougyou-Kanda Bldg 6F, 2-3-4 Uchi-Kanda,
113 * Chiyoda-ku, Tokyo 101-0047, Japan, and JPRS modifies above original code
114 * under following Terms and Conditions set forth by JPNIC.
115 *
116 * JPNIC
117 *
118 * Copyright (c) 2000-2002 Japan Network Information Center. All rights reserved.
119 *
120 * By using this file, you agree to the terms and conditions set forth bellow.
121 *
122 * LICENSE TERMS AND CONDITIONS
123 *
124 * The following License Terms and Conditions apply, unless a different
125 * license is obtained from Japan Network Information Center ("JPNIC"),
126 * a Japanese association, Kokusai-Kougyou-Kanda Bldg 6F, 2-3-4 Uchi-Kanda,
127 * Chiyoda-ku, Tokyo 101-0047, Japan.
128 *
129 * 1. Use, Modification and Redistribution (including distribution of any
130 * modified or derived work) in source and/or binary forms is permitted
131 * under this License Terms and Conditions.
132 *
133 * 2. Redistribution of source code must retain the copyright notices as they
134 * appear in each source code file, this License Terms and Conditions.
135 *
136 * 3. Redistribution in binary form must reproduce the Copyright Notice,
137 * this License Terms and Conditions, in the documentation and/or other
138 * materials provided with the distribution. For the purposes of binary
139 * distribution the "Copyright Notice" refers to the following language:
140 * "Copyright (c) 2000-2002 Japan Network Information Center. All rights reserved."
141 *
142 * 4. The name of JPNIC may not be used to endorse or promote products
143 * derived from this Software without specific prior written approval of
144 * JPNIC.
145 *
146 * 5. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY JPNIC
147 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
148 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
149 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JPNIC BE LIABLE
150 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
151 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
152 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
153 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
154 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
155 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
156 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
157 *
158 *
159 * JPRS Public License Notice
160 * For
161 * idnkit-2.
162 *
163 * The contents of this file are subject to the Terms and Conditions for
164 * the Open Source Code License (the "OSCL"). You may not use this file
165 * except in compliance with above terms and conditions. A copy of the OSCL
166 * is available at <http://jprs.co.jp/idn/>.
167 * The JPRS Revised Code is idnkit-2.
168 * The Initial Developer of the JPRS Revised Code is Japan Network
169 * Information Center ("JPNIC"), a Japanese association,
170 * Kokusai-Kougyou-Kanda Bldg 6F, 2-3-4 Uchi-Kanda, Chiyoda-ku, Tokyo
171 * 101-0047, Japan.
172 * "Copyright (c) 2000-2002 Japan Network Information Center. All rights reserved."
173 * "Copyright (c) 2010-2012 Japan Registry Services Co., Ltd. All rights reserved."
174 * Contributor(s): ______________________________________.
175 *
176 * If you wish to allow use of your version of this file only under the
177 * above License(s) and not to allow others to use your version of this
178 * file, please indicate your decision by deleting the relevant provisions
179 * above and replacing them with the notice and other provisions required
180 * by the above License(s). If you do not delete the relevant provisions,
181 * a recipient may use your version of this file under either the above
182 * License(s).
183 */
184
185 /*
186 * idnconv -- Codeset converter for named.conf and zone files
187 */
188
189 #include <config.h>
190
191 #include <stddef.h>
192 #include <errno.h>
193 #include <stdarg.h>
194 #include <stdio.h>
195 #include <stdlib.h>
196 #include <string.h>
197 #ifdef HAVE_LOCALE_H
198 #include <locale.h>
199 #endif
200
201 #include <idn/result.h>
202 #include <idn/punycode.h>
203 #include <idn/res.h>
204 #include <idn/resconf.h>
205 #include <idn/util.h>
206 #include <idn/utf8.h>
207 #include <idn/version.h>
208
209 #include "strbuf.h"
210 #include "selectiveencode.h"
211
212 /*
213 * Domain name registration / lookup procotols.
214 */
215 typedef enum idnconv_protocol {
216 idnconv_lookup = 0,
217 idnconv_registration = 1
218 } idnconv_protocol_t;
219
220 /*
221 * Parsing result of command line arguments.
222 */
223 typedef struct {
224 char *in_code;
225 char *out_code;
226 char *conf_file;
227 int no_conf;
228 int reverse;
229 idnconv_protocol_t protocol;
230 char *localcheck_file;
231 int line_flush;
232 int whole;
233 int test;
234 idn_action_t skip_actions;
235 char *input_file;
236 } idnconv_option_t;
237
238 /*
239 * Quiet mode flag.
240 */
241 int quiet_mode = 0;
242
243 static void idnconv_option_init(idnconv_option_t *option);
244 static int parse_command_line(int ac, char **av,
245 idnconv_option_t *option);
246 static int parse_skip(const char *s, idn_action_t *action);
247 static int create_resconf(idn_resconf_t *resconf, int no_conf,
248 const char *conf_file,
249 const char *localencoding,
250 const char *localcheck_file);
251 static int encode_file(idn_resconf_t resconf1, idn_resconf_t resconf2,
252 idn_action_t actions, FILE *fp, int line_flush,
253 int whole);
254 static int decode_file(idn_resconf_t resconf1, idn_resconf_t resconf2,
255 idn_action_t actions, FILE *fp, int line_flush,
256 int whole);
257 static idn_result_t
258 encode_line(idnconv_strbuf_t *from, idnconv_strbuf_t *to,
259 idn_resconf_t conf, idn_action_t actions,
260 int whole);
261 static idn_result_t
262 decode_line(idnconv_strbuf_t *from, idnconv_strbuf_t *to,
263 idn_resconf_t conf, idn_action_t actions,
264 int whole);
265 static int trim_newline(idnconv_strbuf_t *buf);
266 static void print_test_status(idn_resconf_t conf1, idn_resconf_t conf2);
267 static void print_version(void);
268 static void print_usage(void);
269 static void errormsg(const char *fmt, ...);
270
271 int
main(int ac,char ** av)272 main(int ac, char **av) {
273 idn_result_t r;
274 int exit_code = 0;
275 idnconv_option_t option;
276 idn_action_t actions;
277 FILE *fp = NULL;
278 idn_resconf_t resconf1 = NULL;
279 idn_resconf_t resconf2 = NULL;
280
281 #ifdef HAVE_SETLOCALE
282 setlocale(LC_ALL, "");
283 #endif
284
285 /*
286 * Parse command line options.
287 */
288 idnconv_option_init(&option);
289 if (!parse_command_line(ac, av, &option)) {
290 errormsg("try 'idnconv2 -help' for more information.\n");
291 exit_code = 1;
292 goto ret;
293 }
294
295 /*
296 * Initialize idnkit library.
297 */
298 r = idn_resconf_initialize();
299 if (r != idn_success) {
300 errormsg("error initializing library\n");
301 exit_code = 1;
302 goto ret;
303 }
304
305 /*
306 * Create two resource contexts; 'resconf1' and 'resconf2'.
307 */
308 if (!create_resconf(&resconf1, option.no_conf, option.conf_file,
309 option.in_code, option.localcheck_file)) {
310 exit_code = 1;
311 goto ret;
312 }
313 if (!create_resconf(&resconf2, option.no_conf, option.conf_file,
314 option.out_code, option.localcheck_file)) {
315 exit_code = 1;
316 goto ret;
317 }
318
319 /*
320 * Determine main actions.
321 */
322 if (option.reverse) {
323 if (option.protocol == idnconv_registration) {
324 actions = IDN_DECODE_REGIST & \
325 ~IDN_UNICODECONV & \
326 ~IDN_LOCALCONV & \
327 ~option.skip_actions;
328 } else {
329 actions = IDN_DECODE_LOOKUP & \
330 ~IDN_UNICODECONV & \
331 ~IDN_LOCALCONV & \
332 ~option.skip_actions;
333 }
334 } else {
335 if (option.protocol == idnconv_registration) {
336 actions = IDN_ENCODE_REGIST & \
337 ~IDN_UNICODECONV & \
338 ~IDN_LOCALCONV & \
339 ~option.skip_actions;
340 } else {
341 actions = IDN_ENCODE_LOOKUP & \
342 ~IDN_UNICODECONV & \
343 ~IDN_LOCALCONV & \
344 ~option.skip_actions;
345 }
346 }
347 if (option.localcheck_file != NULL &&
348 !(option.skip_actions & ~IDN_LOCALCHECK)) {
349 actions |= IDN_LOCALCHECK;
350 }
351 if (option.reverse && option.in_code != NULL &&
352 strcmp(option.in_code, IDN__PUNYCODE_ACENAME) != 0) {
353 actions &= ~IDN_IDNCONV & ~IDN_RTCHECK;
354 }
355 if (!option.reverse && option.out_code != NULL &&
356 strcmp(option.out_code, IDN__PUNYCODE_ACENAME) != 0) {
357 actions &= ~IDN_IDNCONV & ~IDN_RTCHECK & ~IDN_LENCHECK;
358 }
359
360 /*
361 * Test mode.
362 */
363 if (option.test) {
364 print_test_status(resconf1, resconf2);
365 goto ret;
366 }
367
368 /*
369 * Open an input file.
370 */
371 if (option.input_file != NULL) {
372 if ((fp = fopen(option.input_file, "r")) == NULL) {
373 errormsg("failed to open file, %s: %s\n",
374 strerror(errno), av[0]);
375 exit_code = 1;
376 goto ret;
377 }
378 } else {
379 fp = stdin;
380 }
381
382 /*
383 * Do the conversion.
384 */
385 if (option.reverse) {
386 if (!decode_file(resconf1, resconf2, actions, fp,
387 option.line_flush, option.whole)) {
388 exit_code = 1;
389 goto ret;
390 }
391 } else {
392 if (!encode_file(resconf1, resconf2, actions, fp,
393 option.line_flush, option.whole)) {
394 exit_code = 1;
395 goto ret;
396 }
397 }
398
399 ret:
400 if (resconf1 != NULL)
401 idn_resconf_destroy(resconf1);
402 if (resconf2 != NULL)
403 idn_resconf_destroy(resconf2);
404 if (fp != NULL)
405 fclose(fp);
406
407 return (exit_code);
408 }
409
410 static void
idnconv_option_init(idnconv_option_t * option)411 idnconv_option_init(idnconv_option_t *option) {
412 option->in_code = NULL;
413 option->out_code = NULL;
414 option->conf_file = NULL;
415 option->no_conf = 0;
416 option->reverse = 0;
417 option->protocol = idnconv_registration;
418 option->localcheck_file = NULL;
419 option->line_flush = 0;
420 option->whole = 0;
421 option->test = 0;
422 option->skip_actions = 0;
423 option->input_file = NULL;
424 }
425
426 /*
427 * Parse command line arguments.
428 */
429 static int
parse_command_line(int ac,char ** av,idnconv_option_t * option)430 parse_command_line(int ac, char **av, idnconv_option_t *option) {
431 ac--;
432 av++;
433 while (ac > 0 && **av == '-') {
434 if (strcmp(*av, "--") == 0) {
435 ac--;
436 av++;
437 break;
438 } else if (strcmp(*av, "-in") == 0 ||
439 strcmp(*av, "-i") == 0) {
440 if (ac < 2) {
441 errormsg("option '%s' requires an argument.\n", *av);
442 return (0);
443 }
444 option->in_code = av[1];
445 ac--;
446 av++;
447 } else if (strcmp(*av, "-out") == 0 ||
448 strcmp(*av, "-o") == 0) {
449 if (ac < 2) {
450 errormsg("option '%s' requires an argument.\n", *av);
451 return (0);
452 }
453 option->out_code = av[1];
454 ac--;
455 av++;
456 } else if (strcmp(*av, "-conf") == 0 ||
457 strcmp(*av, "-c") == 0) {
458 if (ac < 2) {
459 errormsg("option '%s' requires an argument.\n", *av);
460 return (0);
461 }
462 option->conf_file = av[1];
463 ac--;
464 av++;
465 } else if (strcmp(*av, "-noconf") == 0 ||
466 strcmp(*av, "-C") == 0) {
467 option->no_conf = 1;
468 } else if (strcmp(*av, "-reverse") == 0 ||
469 strcmp(*av, "-r") == 0) {
470 option->reverse = 1;
471 } else if (strcmp(*av, "-registration") == 0 ||
472 strcmp(*av, "-g") == 0) {
473 option->protocol = idnconv_registration;
474 } else if (strcmp(*av, "-lookup") == 0 ||
475 strcmp(*av, "-l") == 0) {
476 option->protocol = idnconv_lookup;
477 } else if (strcmp(*av, "-nomap") == 0 ||
478 strcmp(*av, "-M") == 0) {
479 if (!parse_skip("map", &option->skip_actions))
480 return (0);
481 } else if (strcmp(*av, "-skip") == 0) {
482 if (ac < 2) {
483 errormsg("option '%s' requires an argument.\n", *av);
484 return (0);
485 }
486 if (!parse_skip(av[1], &option->skip_actions)) {
487 errormsg("invalid argument to option '-skip'.\n", *av);
488 return (0);
489 }
490 ac--;
491 av++;
492 } else if (strcmp(*av, "-localcheck") == 0 ||
493 strcmp(*av, "-e") == 0) {
494 if (ac < 2) {
495 errormsg("option '%s' requires an argument.\n", *av);
496 return (0);
497 }
498 option->localcheck_file = av[1];
499 ac--;
500 av++;
501 } else if (strcmp(*av, "-flush") == 0) {
502 option->line_flush = 1;
503 } else if (strcmp(*av, "-whole") == 0 ||
504 strcmp(*av, "-w") == 0) {
505 option->whole = 1;
506 } else if (strcmp(*av, "-test") == 0 ||
507 strcmp(*av, "-t") == 0) {
508 option->test = 1;
509 } else if (strcmp(*av, "-version") == 0 ||
510 strcmp(*av, "-v") == 0) {
511 print_version();
512 exit(0);
513 } else if (strcmp(*av, "-help") == 0 ||
514 strcmp(*av, "-h") == 0) {
515 print_usage();
516 exit(0);
517 } else if (strcmp(*av, "-alias-file") == 0 ||
518 strcmp(*av, "-a") == 0 ||
519 strcmp(*av, "-nameprep") == 0 ||
520 strcmp(*av, "-n") == 0 ||
521 strcmp(*av, "-localmap") == 0 ||
522 strcmp(*av, "-delimiter") == 0) {
523 if (ac < 2) {
524 errormsg("option '%s' requires an argument.\n", *av);
525 return (0);
526 }
527 errormsg("warning: ignore obsolete option '%s'.\n", *av);
528 ac--;
529 av++;
530 } else if (strcmp(*av, "-nonameprep") == 0 ||
531 strcmp(*av, "-N") == 0 ||
532 strcmp(*av, "-nolocalmap") == 0 ||
533 strcmp(*av, "-L") == 0 ||
534 strcmp(*av, "-nounassigncheck") == 0 ||
535 strcmp(*av, "-U") == 0 ||
536 strcmp(*av, "-noasciicheck") == 0 ||
537 strcmp(*av, "-A") == 0 ||
538 strcmp(*av, "-nobidicheck") == 0 ||
539 strcmp(*av, "-B") == 0 ||
540 strcmp(*av, "-noroundtripcheck") == 0 ||
541 strcmp(*av, "-nolengthcheck") == 0) {
542 errormsg("ignore obsolete option '%s'.\n", *av);
543 } else {
544 errormsg("unrecognized option '%s'.\n", *av);
545 return (0);
546 }
547 ac--;
548 av++;
549 }
550
551 if (ac > 1 && !option->test) {
552 errormsg("too many arguments.\n");
553 return (0);
554 }
555
556 if (ac == 0)
557 option->input_file = NULL;
558 else
559 option->input_file = av[0];
560
561 /*
562 * Resolve conflicts between options.
563 */
564 if (option->no_conf && option->conf_file != NULL)
565 option->conf_file = NULL;
566 if (option->reverse &&
567 option->out_code != NULL &&
568 strcmp(option->out_code, IDN__PUNYCODE_ACENAME) == 0) {
569 option->reverse = 0;
570 }
571
572 return (1);
573 }
574
575 struct idnconv_actionmap {
576 char *name;
577 idn_action_t value;
578 };
579
580 static const struct idnconv_actionmap action_maps[] = {
581 {"map", IDN_MAP},
582 {"asclower", IDN_ASCLOWER},
583 {"rtconv", IDN_RTCONV},
584 {"prohcheck", IDN_PROHCHECK},
585 {"unascheck", IDN_UNASCHECK},
586 {"nfccheck", IDN_NFCCHECK},
587 {"prefcheck", IDN_PREFCHECK},
588 {"hyphcheck", IDN_HYPHCHECK},
589 {"combcheck", IDN_COMBCHECK},
590 {"ctxjcheck", IDN_CTXJCHECK},
591 {"ctxocheck", IDN_CTXOCHECK},
592 {"ctxolitecheck", IDN_CTXOLITECHECK},
593 {"bidicheck", IDN_BIDICHECK},
594 {"idnconv", IDN_IDNCONV},
595 {"lencheck", IDN_LENCHECK},
596 {"rtcheck", IDN_RTCHECK},
597 {NULL, 0}
598 };
599
600 static int
parse_skip(const char * arg,idn_action_t * action)601 parse_skip(const char *arg, idn_action_t *action) {
602 const struct idnconv_actionmap *mapp;
603 int found;
604
605 while (*arg != '\0') {
606 found = 0;
607 for (mapp = action_maps; mapp->name != NULL; mapp++) {
608 const char *s1 = arg;
609 const char *s2 = mapp->name;
610
611 while (*s1 != ',' && *s1 != '\0') {
612 if (*s1 != *s2 || *s2 == '\0')
613 break;
614 s1++;
615 s2++;
616 }
617 if (*s1 == ',' && *s2 == '\0') {
618 if (*(s1 + 1) == '\0')
619 return (0);
620 *action |= mapp->value;
621 arg = s1 + 1;
622 found = 1;
623 break;
624 } else if (*s1 == '\0' && *s2 == '\0') {
625 *action |= mapp->value;
626 arg = s1;
627 found = 1;
628 break;
629 }
630 }
631 if (!found)
632 return (0);
633 }
634 return (1);
635 }
636
637 static int
create_resconf(idn_resconf_t * resconf,int no_conf,const char * conf_file,const char * localencoding,const char * localcheck_file)638 create_resconf(idn_resconf_t *resconf, int no_conf,
639 const char *conf_file, const char *localencoding,
640 const char *localcheck_file) {
641 idn_result_t r;
642
643 r = idn_resconf_create(resconf);
644 if (r != idn_success) {
645 errormsg("failed to initialize configuration contexts, %s\n",
646 idn_result_tostring(r));
647 return (0);
648 }
649
650 if (!no_conf) {
651 r = idn_resconf_loadfile(*resconf, conf_file);
652 if (r != idn_success &&
653 (r != idn_nofile || conf_file != NULL)) {
654 errormsg("failed to read a configuration file, %s\n",
655 idn_result_tostring(r));
656 return (0);
657 }
658 }
659
660 /*
661 * For backward compatibility to idnkit-1.0, 'Punycode' specified
662 * as local encoding, we assumes it as UTF-8.
663 */
664 if (localencoding != NULL &&
665 strcmp(localencoding, IDN__PUNYCODE_ACENAME) == 0)
666 localencoding = IDN__UTF8_ENCODINGNAME;
667
668 r = idn_resconf_setlocalencoding(*resconf, localencoding);
669 if (r != idn_success) {
670 errormsg("failed to set the local encoding, %s\n",
671 idn_result_tostring(r));
672 return (0);
673 }
674
675 r = idn_resconf_setlocalcheckfile(*resconf, localcheck_file);
676 if (r != idn_success) {
677 errormsg("failed to set the localcheck file, %s\n",
678 idn_result_tostring(r));
679 return (0);
680 }
681
682 return (1);
683 }
684
685 static int
encode_file(idn_resconf_t resconf1,idn_resconf_t resconf2,idn_action_t actions,FILE * fp,int line_flush,int whole)686 encode_file(idn_resconf_t resconf1, idn_resconf_t resconf2,
687 idn_action_t actions, FILE *fp, int line_flush, int whole) {
688 idn_result_t r;
689 idnconv_strbuf_t buf1, buf2;
690 int nl_trimmed;
691 int lineno;
692
693 strbuf_init(&buf1);
694 strbuf_init(&buf2);
695 lineno = 0;
696 while (strbuf_getline(&buf1, fp) != NULL) {
697 lineno++;
698
699 /*
700 * Trim newline at the end.
701 */
702 nl_trimmed = trim_newline(&buf1);
703
704 /*
705 * Convert an input line to UTF-8.
706 */
707 r = decode_line(&buf1, &buf2, resconf1, IDN_UNICODECONV, 1);
708 if (r != idn_success) {
709 errormsg("conversion failed at line %d: %s\n",
710 lineno, idn_result_tostring(r));
711 goto err;
712 }
713 if (!idn__utf8_isvalidstring(strbuf_get(&buf2))) {
714 errormsg("conversion to utf-8 failed at line %d\n",
715 lineno);
716 goto err;
717 }
718
719 /*
720 * Perform an encoding process.
721 */
722 r = encode_line(&buf2, &buf1, resconf1, actions, whole);
723 if (r != idn_success) {
724 errormsg("conversion failed at line %d: %s\n",
725 lineno, idn_result_tostring(r));
726 goto err;
727 }
728
729 /*
730 * Finally, convert the encoded string to the '-out' encoding.
731 */
732 r = encode_line(&buf1, &buf2, resconf2, IDN_LOCALCONV, 1);
733
734 if (r != idn_success) {
735 errormsg("conversion failed at line %d: %s\n",
736 lineno, idn_result_tostring(r));
737 goto err;
738 }
739
740 fputs(strbuf_get(&buf2), stdout);
741 if (nl_trimmed)
742 putc('\n', stdout);
743
744 if (line_flush)
745 fflush(stdout);
746 }
747
748 strbuf_reset(&buf1);
749 strbuf_reset(&buf2);
750 return (1);
751
752 err:
753 strbuf_reset(&buf1);
754 strbuf_reset(&buf2);
755 return (0);
756 }
757
758 static int
decode_file(idn_resconf_t resconf1,idn_resconf_t resconf2,idn_action_t actions,FILE * fp,int line_flush,int whole)759 decode_file(idn_resconf_t resconf1, idn_resconf_t resconf2,
760 idn_action_t actions, FILE *fp, int line_flush, int whole) {
761 idn_result_t r;
762 idnconv_strbuf_t buf1, buf2;
763 int nl_trimmed;
764 int lineno;
765
766 strbuf_init(&buf1);
767 strbuf_init(&buf2);
768 lineno = 1;
769 while (strbuf_getline(&buf1, fp) != NULL) {
770 /*
771 * Trim newline at the end.
772 */
773 nl_trimmed = trim_newline(&buf1);
774
775 /*
776 * Convert an input line to UTF-8.
777 */
778 r = decode_line(&buf1, &buf2, resconf1, IDN_UNICODECONV, 1);
779 if (r != idn_success) {
780 errormsg("conversion failed at line %d: %s\n",
781 lineno, idn_result_tostring(r));
782 goto err;
783 }
784 if (!idn__utf8_isvalidstring(strbuf_get(&buf2))) {
785 errormsg("conversion to utf-8 failed at line %d\n",
786 lineno);
787 goto err;
788 }
789
790 /*
791 * Convert internationalized domain names in the line.
792 */
793 r = decode_line(&buf2, &buf1, resconf1, actions, whole);
794 if (r != idn_success) {
795 errormsg("conversion failed at line %d: %s\n",
796 lineno, idn_result_tostring(r));
797 goto err;
798 }
799
800 /*
801 * Finally, convert the encoded string to the '-out' encoding.
802 */
803 r = encode_line(&buf1, &buf2, resconf2, IDN_LOCALCONV, 1);
804 if (r != idn_success) {
805 errormsg("conversion failed at line %d: %s\n",
806 lineno, idn_result_tostring(r));
807 goto err;
808 }
809
810 fputs(strbuf_get(&buf2), stdout);
811 if (nl_trimmed)
812 putc('\n', stdout);
813
814 if (line_flush)
815 fflush(stdout);
816
817 lineno++;
818 }
819 strbuf_reset(&buf1);
820 strbuf_reset(&buf2);
821 return (1);
822
823 err:
824 strbuf_reset(&buf1);
825 strbuf_reset(&buf2);
826 return (0);
827 }
828
829 static int
trim_newline(idnconv_strbuf_t * buf)830 trim_newline(idnconv_strbuf_t *buf) {
831 /*
832 * If the string in BUF ends with a newline, trim it and
833 * return 1. Otherwise, just return 0 without modifying BUF.
834 */
835 char *s = strbuf_get(buf);
836 size_t len = strlen(s);
837
838 if (s[len - 1] == '\n') {
839 s[len - 1] = '\0';
840 return (1);
841 }
842
843 return (0);
844 }
845
846 idn_result_t
encode_line(idnconv_strbuf_t * from,idnconv_strbuf_t * to,idn_resconf_t conf,idn_action_t actions,int whole)847 encode_line(idnconv_strbuf_t *from, idnconv_strbuf_t *to,
848 idn_resconf_t conf, idn_action_t actions, int whole) {
849 idn_result_t r = idn_success;
850 char *from_str = strbuf_get(from);
851
852 for (;;) {
853 char *to_str = strbuf_get(to);
854 size_t to_size = strbuf_size(to);
855
856 if (whole) {
857 r = idn_res_encodename(conf, actions, from_str,
858 to_str, to_size);
859 } else {
860 r = selective_encode(conf, actions, from_str,
861 to_str, to_size);
862 }
863 if (r == idn_buffer_overflow) {
864 /*
865 * Conversion is not successful because
866 * the size of the target buffer is not enough.
867 * Double the size and retry.
868 */
869 if (strbuf_double(to) == NULL) {
870 /* oops. allocation failed. */
871 return (idn_nomemory);
872 }
873 } else {
874 break;
875 }
876 }
877 return (r);
878 }
879
880 static idn_result_t
decode_line(idnconv_strbuf_t * from,idnconv_strbuf_t * to,idn_resconf_t conf,idn_action_t actions,int whole)881 decode_line(idnconv_strbuf_t *from, idnconv_strbuf_t *to,
882 idn_resconf_t conf, idn_action_t actions, int whole) {
883 idn_result_t r = idn_success;
884 char *from_str = strbuf_get(from);
885
886 for (;;) {
887 char *to_str = strbuf_get(to);
888 size_t to_size = strbuf_size(to);
889
890 if (whole) {
891 r = idn_res_decodename(conf, actions, from_str,
892 to_str, to_size);
893 } else {
894 r = selective_decode(conf, actions, from_str,
895 to_str, to_size);
896 }
897 if (r == idn_buffer_overflow) {
898 /*
899 * Conversion is not successful because
900 * the size of the target buffer is not enough.
901 * Double the size and retry.
902 */
903 if (strbuf_double(to) == NULL) {
904 /* oops. allocation failed. */
905 return (idn_nomemory);
906 }
907 } else {
908 break;
909 }
910 }
911 return (r);
912 }
913
914 static char *options[] = {
915 "Usage: idnconv2 [options..] [file]",
916 " -in INPUT-CODESET : specifies input codeset name.",
917 " -i INPUT-CODESET : synonym for -in",
918 " -out OUTPUT-CODESET : specifies output codeset name.",
919 " -o OUTPUT-CODESET : synonym for -out",
920 " -conf CONF-FILE : specifies idnkit configuration file.",
921 " -c CONF-FILE : synonym for -conf",
922 " -noconf : do not load idnkit configuration file.",
923 " -C : synonym for -noconf",
924 " -reverse : specifies reverse conversion.",
925 " (i.e. IDN encoding to local encoding)",
926 " -r : synonym for -reverse",
927 " -registration : convert regions with the Domain Name Registration",
928 " protocol. (default)",
929 " -g : synonym for -registration",
930 " -lookup : convert regions with the Domain Name Lookup",
931 " protocol.",
932 " -l : synonym for -lookup",
933 " -nomap : do not perform mappings.",
934 " -M : synonym for -nomap",
935 " -skip ACTION,... : do not perform ACTION.",
936 " the following action names are recognized:",
937 " map : mappings",
938 " asclower : ASCII uppercase letters to lowercase",
939 " rtconv : conversion from Punycode to Unicode",
940 " for round trip check",
941 " prohcheck : prohibited code point check",
942 " unascheck : unassigned code point check",
943 " nfccheck : NFC conformance check",
944 " prefcheck : ACE prefix check",
945 " hyphcheck : hyphen check",
946 " combcheck : combining character check",
947 " ctxjcheck : CONTEXTJ code point check",
948 " ctxocheck : CONTEXTO code point check for the",
949 " Domain Name Registration protocol",
950 " ctxolitecheck",
951 " : CONTEXTO code point check for the",
952 " Domain Name Lookup protocol",
953 " bidicheck : Bidi check",
954 " idnconv : conversion from/to Punycode",
955 " lencheck : label length check",
956 " rtcheck : round trip check",
957 " -localcheck FILE : perform local check with a code point table FILE.",
958 " -e FILE : synonym for -localcheck",
959 " -flush : line-buffering mode.",
960 " -whole : convert the whole region instead of",
961 " regions containing non-ascii characters.",
962 " -w : synonym for -whole",
963 " -test : output language and local encoding status,",
964 " then exit.",
965 " -t synonym for -test",
966 " -version : print version number, then exit.",
967 " -v : synonym for -version",
968 " -help : print this help, then exit.",
969 " -h : synonym for -help",
970 NULL
971 };
972
973 static void
print_test_status(idn_resconf_t conf1,idn_resconf_t conf2)974 print_test_status(idn_resconf_t conf1, idn_resconf_t conf2) {
975 printf("language = %s\n", idn_resconf_getlanguage(conf1));
976 printf("input encoding = %s\n", idn_resconf_getlocalencoding(conf1));
977 printf("output encoding = %s\n", idn_resconf_getlocalencoding(conf2));
978 }
979
980 static void
print_version(void)981 print_version(void) {
982 printf("idnconv (idnkit) version: %s\n", IDNKIT_VERSION);
983 printf("%s\n", idn_version_getstring());
984 }
985
986 static void
print_usage(void)987 print_usage(void) {
988 int i;
989
990 for (i = 0; options[i] != NULL; i++)
991 printf("%s\n", options[i]);
992 }
993
994 static void
errormsg(const char * fmt,...)995 errormsg(const char *fmt, ...) {
996 va_list args;
997
998 if (quiet_mode)
999 return;
1000
1001 va_start(args, fmt);
1002 vfprintf(stderr, fmt, args);
1003 va_end(args);
1004 }
1005