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