1#ifndef lint
2static char *rcsid = "$Id: resconf.c,v 1.1 2003/06/04 00:26:12 marka Exp $";
3#endif
4
5/*
6 * Copyright (c) 2000 Japan Network Information Center.  All rights reserved.
7 *
8 * By using this file, you agree to the terms and conditions set forth bellow.
9 *
10 * 			LICENSE TERMS AND CONDITIONS
11 *
12 * The following License Terms and Conditions apply, unless a different
13 * license is obtained from Japan Network Information Center ("JPNIC"),
14 * a Japanese association, Kokusai-Kougyou-Kanda Bldg 6F, 2-3-4 Uchi-Kanda,
15 * Chiyoda-ku, Tokyo 101-0047, Japan.
16 *
17 * 1. Use, Modification and Redistribution (including distribution of any
18 *    modified or derived work) in source and/or binary forms is permitted
19 *    under this License Terms and Conditions.
20 *
21 * 2. Redistribution of source code must retain the copyright notices as they
22 *    appear in each source code file, this License Terms and Conditions.
23 *
24 * 3. Redistribution in binary form must reproduce the Copyright Notice,
25 *    this License Terms and Conditions, in the documentation and/or other
26 *    materials provided with the distribution.  For the purposes of binary
27 *    distribution the "Copyright Notice" refers to the following language:
28 *    "Copyright (c) 2000-2002 Japan Network Information Center.  All rights reserved."
29 *
30 * 4. The name of JPNIC may not be used to endorse or promote products
31 *    derived from this Software without specific prior written approval of
32 *    JPNIC.
33 *
34 * 5. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY JPNIC
35 *    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
36 *    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
37 *    PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL JPNIC BE LIABLE
38 *    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
39 *    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
40 *    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
41 *    BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
42 *    WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
43 *    OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
44 *    ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
45 */
46
47#include <config.h>
48
49#include <stddef.h>
50#include <stdarg.h>
51#include <stdlib.h>
52#include <stdio.h>
53#include <string.h>
54#include <ctype.h>
55#include <errno.h>
56#ifdef HAVE_UNISTD_H
57#include <unistd.h>
58#endif
59#ifdef HAVE_PWD_H
60#include <pwd.h>
61#endif
62
63#include <idn/result.h>
64#include <idn/assert.h>
65#include <idn/logmacro.h>
66#include <idn/converter.h>
67#include <idn/nameprep.h>
68#include <idn/normalizer.h>
69#include <idn/checker.h>
70#include <idn/mapper.h>
71#include <idn/mapselector.h>
72#include <idn/delimitermap.h>
73#include <idn/localencoding.h>
74#include <idn/resconf.h>
75#include <idn/debug.h>
76#include <idn/util.h>
77
78#ifdef WIN32
79#define MAX_PATH_SIZE		500	/* a good longer than MAX_PATH */
80#define IDNVAL_CONFFILE		"ConfFile"
81#else /* WIN32 */
82
83#ifndef IDN_RESCONF_DIR
84#define IDN_RESCONF_DIR		"/etc"
85#endif
86#define IDN_RESCONF_FILE	IDN_RESCONF_DIR "/idn.conf"
87#define IDN_USER_RESCONF_FILE	"/.idnrc"
88
89#endif /* WIN32 */
90
91#define MAX_CONF_LINE_LENGTH	255
92#define MAX_CONF_LINE_ARGS	63
93
94#define DEFAULT_CONF_NAMEPREP		0x0001
95#define DEFAULT_CONF_IDN_ENCODING	0x0010
96#define DEFAULT_CONF_ALL		(DEFAULT_CONF_NAMEPREP | \
97					DEFAULT_CONF_IDN_ENCODING)
98
99#define IDN_ENCODING_CURRENT	"Punycode"
100
101#ifdef ENABLE_MDNKIT_COMPAT
102#define MDN_RESCONF_FILE	IDN_RESCONF_DIR "/mdn.conf"
103#endif
104
105struct idn_resconf {
106	int local_converter_is_static;
107	idn_converter_t local_converter;
108	idn_converter_t idn_converter;
109        idn_converter_t aux_idn_converter;
110	idn_normalizer_t normalizer;
111	idn_checker_t prohibit_checker;
112	idn_checker_t unassigned_checker;
113	idn_checker_t bidi_checker;
114	idn_mapper_t mapper;
115	idn_mapselector_t local_mapper;
116	idn_delimitermap_t delimiter_mapper;
117	int reference_count;
118};
119
120static int initialized;
121
122#ifndef WIN32
123static const char *	userhomedir(void);
124#endif
125static idn_result_t	open_userdefaultfile(FILE **fpp);
126static idn_result_t	open_defaultfile(FILE **fpp);
127static idn_result_t	parse_conf(idn_resconf_t ctx, FILE *fp);
128static idn_result_t	parse_idn_encoding(idn_resconf_t ctx, char *args,
129					   int lineno);
130static idn_result_t	parse_local_map(idn_resconf_t ctx, char *args,
131					int lineno);
132static idn_result_t	parse_nameprep(idn_resconf_t ctx, char *args,
133				       int lineno);
134static int		split_args(char *s, char **av, int max_ac);
135static void		resetconf(idn_resconf_t ctx);
136#ifndef WITHOUT_ICONV
137static idn_result_t	update_local_converter(idn_resconf_t ctx);
138#endif
139static idn_result_t	setdefaults_body(idn_resconf_t ctx, int conf_mask);
140
141idn_result_t
142idn_resconf_initialize(void) {
143	idn_result_t r;
144
145	TRACE(("idn_resconf_initialize()\n"));
146
147	if (initialized) {
148		r = idn_success;
149		goto ret;
150	}
151
152	/*
153	 * Initialize sub modules.
154	 */
155	if ((r = idn_converter_initialize()) != idn_success)
156		goto ret;
157	if ((r = idn_normalizer_initialize()) != idn_success)
158		goto ret;
159	if ((r = idn_checker_initialize()) != idn_success)
160		goto ret;
161	if ((r = idn_mapselector_initialize()) != idn_success)
162		goto ret;
163	if ((r = idn_mapper_initialize()) != idn_success)
164		goto ret;
165
166	r = idn_success;
167	initialized = 1;
168ret:
169	TRACE(("idn_resconf_initialize(): %s\n", idn_result_tostring(r)));
170	return (r);
171}
172
173idn_result_t
174idn_resconf_create(idn_resconf_t *ctxp) {
175	idn_resconf_t ctx = NULL;
176	idn_result_t r;
177
178	assert(ctxp != NULL);
179
180	TRACE(("idn_resconf_create()\n"));
181
182	if (!initialized) {
183		r = idn_failure;
184		goto ret;
185	}
186	if ((ctx = malloc(sizeof(*ctx))) == NULL) {
187		r = idn_nomemory;
188		goto ret;
189	}
190
191	ctx->local_converter_is_static = 0;
192	ctx->local_converter = NULL;
193	ctx->idn_converter = NULL;
194	ctx->aux_idn_converter = NULL;
195	ctx->normalizer = NULL;
196	ctx->prohibit_checker = NULL;
197	ctx->unassigned_checker = NULL;
198	ctx->bidi_checker = NULL;
199	ctx->mapper = NULL;
200	ctx->local_mapper = NULL;
201	ctx->reference_count = 1;
202
203	r = idn_delimitermap_create(&ctx->delimiter_mapper);
204	if (r != idn_success)
205		goto ret;
206
207	*ctxp = ctx;
208	r = idn_success;
209ret:
210	TRACE(("idn_resconf_create(): %s\n", idn_result_tostring(r)));
211	return (r);
212}
213
214char *
215idn_resconf_defaultfile() {
216#ifdef WIN32
217	static char default_path[MAX_PATH_SIZE];
218
219	if (idn__util_getregistrystring(idn__util_hkey_localmachine,
220					IDNVAL_CONFFILE, default_path,
221					sizeof(default_path))) {
222		return (default_path);
223	} else {
224		return (NULL);
225	}
226#else
227	return (IDN_RESCONF_FILE);
228#endif
229}
230
231#ifndef WIN32
232static const char *
233userhomedir() {
234	uid_t uid;
235	struct passwd *pwd;
236
237	uid = getuid();
238	pwd = getpwuid(uid);
239	if (pwd == NULL) {
240		return (NULL);
241	}
242
243	return (pwd->pw_dir);
244}
245#endif
246
247static idn_result_t
248open_userdefaultfile(FILE **fpp) {
249#ifdef WIN32
250	char user_path[MAX_PATH_SIZE];
251
252	TRACE(("open_userdefaultfile()\n"));
253
254	if (idn__util_getregistrystring(idn__util_hkey_currentuser,
255					IDNVAL_CONFFILE, user_path,
256					sizeof(user_path)) == 0) {
257		return (idn_nofile);
258	}
259	*fpp = fopen(user_path, "r");
260	if (*fpp == NULL) {
261		return (idn_nofile);
262	}
263	return (idn_success);
264#else /* WIN32 */
265	const char *homedir;
266	char *file;
267	int len;
268
269	TRACE(("open_userdefaultfile()\n"));
270
271	homedir = userhomedir();
272	len = strlen(IDN_USER_RESCONF_FILE) + 1;
273	if (homedir != NULL) {
274		len += strlen(homedir);
275	} else {
276		return (idn_notfound);
277	}
278
279	file = (char *)malloc(sizeof(char) * len);
280	if (file == NULL) {
281		WARNING(("open_userdefaultfile(): malloc failed\n"));
282		return (idn_nomemory);
283	}
284
285	(void)strcpy(file, homedir);
286	strcat(file, IDN_USER_RESCONF_FILE);
287
288	*fpp = fopen(file, "r");
289	free(file);
290	if (*fpp == NULL) {
291		return (idn_nofile);
292	}
293
294	return (idn_success);
295#endif /* WIN32 */
296}
297
298static idn_result_t
299open_defaultfile(FILE **fpp) {
300	idn_result_t r;
301	const char *file;
302
303	r = open_userdefaultfile(fpp);
304	if (r == idn_nofile || r == idn_notfound) {
305		TRACE(("open_defaultfile: "
306		       "cannot open user configuration file\n"));
307		file = idn_resconf_defaultfile();
308		*fpp = fopen(file, "r");
309#ifdef ENABLE_MDNKIT_COMPAT
310		if (*fpp == NULL)
311			*fpp = fopen(MDN_RESCONF_FILE, "r");
312#endif
313		if (*fpp == NULL) {
314			TRACE(("open_defaultfile: "
315			       "cannot open system configuration file\n"));
316			return (idn_nofile);
317		}
318	} else if (r != idn_success) {
319		return (r);
320	}
321
322	return (idn_success);
323}
324
325idn_result_t
326idn_resconf_loadfile(idn_resconf_t ctx, const char *file) {
327	FILE *fp = NULL;
328	idn_result_t r;
329
330	assert(ctx != NULL);
331
332	TRACE(("idn_resconf_loadfile(file=%s)\n",
333	      file == NULL ? "<null>" : file));
334
335	resetconf(ctx);
336	r = idn_delimitermap_create(&ctx->delimiter_mapper);
337	if (r != idn_success) {
338		goto ret;
339	}
340
341	if (file == NULL) {
342		r = open_defaultfile(&fp);
343		if (r == idn_nofile || r == idn_notfound) {
344			r = setdefaults_body(ctx, 0);
345			goto ret;
346		} else if (r != idn_success) {
347			goto ret;
348		}
349	} else {
350		fp = fopen(file, "r");
351		if (fp == NULL) {
352			TRACE(("idn_resconf_loadfile: cannot open %-.40s\n",
353			       file));
354			r = idn_nofile;
355			goto ret;
356		}
357	}
358
359	r = parse_conf(ctx, fp);
360	fclose(fp);
361
362ret:
363	TRACE(("idn_resconf_loadfile(): %s\n", idn_result_tostring(r)));
364	return (r);
365}
366
367void
368idn_resconf_destroy(idn_resconf_t ctx) {
369	assert(ctx != NULL);
370
371	TRACE(("idn_resconf_destroy()\n"));
372
373	ctx->reference_count--;
374	if (ctx->reference_count <= 0) {
375		resetconf(ctx);
376		free(ctx);
377		TRACE(("idn_resconf_destroy: the object is destroyed\n"));
378	} else {
379		TRACE(("idn_resconf_destroy(): "
380		       "update reference count (%d->%d)\n",
381		       ctx->reference_count + 1, ctx->reference_count));
382	}
383}
384
385void
386idn_resconf_incrref(idn_resconf_t ctx) {
387	assert(ctx != NULL);
388
389	TRACE(("idn_resconf_incrref()\n"));
390	TRACE(("idn_resconf_incrref: update reference count (%d->%d)\n",
391		ctx->reference_count, ctx->reference_count + 1));
392
393	ctx->reference_count++;
394}
395
396idn_converter_t
397idn_resconf_getalternateconverter(idn_resconf_t ctx) {
398	assert(ctx != NULL);
399
400	TRACE(("idn_resconf_getalternateconverter()\n"));
401
402	return (idn_resconf_getidnconverter(ctx));
403}
404
405idn_delimitermap_t
406idn_resconf_getdelimitermap(idn_resconf_t ctx) {
407	assert(ctx != NULL);
408
409	TRACE(("idn_resconf_getdelimitermap()\n"));
410
411	if (ctx->delimiter_mapper != NULL)
412		idn_delimitermap_incrref(ctx->delimiter_mapper);
413	return (ctx->delimiter_mapper);
414}
415
416idn_converter_t
417idn_resconf_getidnconverter(idn_resconf_t ctx) {
418	assert(ctx != NULL);
419
420	TRACE(("idn_resconf_getidnconverter()\n"));
421
422	if (ctx->idn_converter != NULL)
423		idn_converter_incrref(ctx->idn_converter);
424	return (ctx->idn_converter);
425}
426
427idn_converter_t
428idn_resconf_getauxidnconverter(idn_resconf_t ctx) {
429	assert(ctx != NULL);
430
431	TRACE(("idn_resconf_getauxidnconverter()\n"));
432
433	if (ctx->aux_idn_converter != NULL)
434		idn_converter_incrref(ctx->aux_idn_converter);
435	return (ctx->aux_idn_converter);
436}
437
438idn_converter_t
439idn_resconf_getlocalconverter(idn_resconf_t ctx) {
440	assert(ctx != NULL);
441
442	TRACE(("idn_resconf_getlocalconverter()\n"));
443
444#ifdef WITHOUT_ICONV
445	return NULL;
446
447#else /* WITHOUT_ICONV */
448	if (update_local_converter(ctx) != idn_success)
449		return (NULL);
450
451	idn_converter_incrref(ctx->local_converter);
452	return (ctx->local_converter);
453
454#endif /* WITHOUT_ICONV */
455}
456
457idn_mapselector_t
458idn_resconf_getlocalmapselector(idn_resconf_t ctx) {
459	assert(ctx != NULL);
460
461	TRACE(("idn_resconf_getlocalmapselector()\n"));
462
463	if (ctx->local_mapper != NULL)
464		idn_mapselector_incrref(ctx->local_mapper);
465	return (ctx->local_mapper);
466}
467
468idn_mapper_t
469idn_resconf_getmapper(idn_resconf_t ctx) {
470	assert(ctx != NULL);
471
472	TRACE(("idn_resconf_getmapper()\n"));
473
474	if (ctx->mapper != NULL)
475		idn_mapper_incrref(ctx->mapper);
476	return (ctx->mapper);
477}
478
479idn_normalizer_t
480idn_resconf_getnormalizer(idn_resconf_t ctx) {
481	assert(ctx != NULL);
482
483	TRACE(("idn_resconf_getnormalizer()\n"));
484
485	if (ctx->normalizer != NULL)
486		idn_normalizer_incrref(ctx->normalizer);
487	return (ctx->normalizer);
488}
489
490idn_checker_t
491idn_resconf_getprohibitchecker(idn_resconf_t ctx) {
492	assert(ctx != NULL);
493
494	TRACE(("idn_resconf_getprohibitchecker()\n"));
495
496	if (ctx->prohibit_checker != NULL)
497		idn_checker_incrref(ctx->prohibit_checker);
498	return (ctx->prohibit_checker);
499}
500
501idn_checker_t
502idn_resconf_getunassignedchecker(idn_resconf_t ctx) {
503	assert(ctx != NULL);
504
505	TRACE(("idn_resconf_getunassignedchecker()\n"));
506
507	if (ctx->unassigned_checker != NULL)
508		idn_checker_incrref(ctx->unassigned_checker);
509	return (ctx->unassigned_checker);
510}
511
512idn_checker_t
513idn_resconf_getbidichecker(idn_resconf_t ctx) {
514	assert(ctx != NULL);
515
516	TRACE(("idn_resconf_getbidichecker()\n"));
517
518	if (ctx->bidi_checker != NULL)
519		idn_checker_incrref(ctx->bidi_checker);
520	return (ctx->bidi_checker);
521}
522
523void
524idn_resconf_setalternateconverter(idn_resconf_t ctx,
525				  idn_converter_t alternate_converter) {
526	assert(ctx != NULL);
527
528	TRACE(("idn_resconf_setalternateconverter()\n"));
529}
530
531void
532idn_resconf_setdelimitermap(idn_resconf_t ctx,
533			    idn_delimitermap_t delimiter_mapper) {
534	assert(ctx != NULL);
535
536	TRACE(("idn_resconf_setdelimitermap()\n"));
537
538	if (ctx->delimiter_mapper != NULL)
539		idn_delimitermap_destroy(ctx->delimiter_mapper);
540	ctx->delimiter_mapper = delimiter_mapper;
541	if (delimiter_mapper != NULL)
542		idn_delimitermap_incrref(ctx->delimiter_mapper);
543}
544
545void
546idn_resconf_setidnconverter(idn_resconf_t ctx,
547			    idn_converter_t idn_converter) {
548	assert(ctx != NULL);
549
550	TRACE(("idn_resconf_setidnconverter()\n"));
551
552	if (ctx->idn_converter != NULL)
553		idn_converter_destroy(ctx->idn_converter);
554	ctx->idn_converter = idn_converter;
555	if (idn_converter != NULL)
556		idn_converter_incrref(ctx->idn_converter);
557}
558
559void
560idn_resconf_setauxidnconverter(idn_resconf_t ctx,
561				idn_converter_t aux_idn_converter) {
562	assert(ctx != NULL);
563
564	TRACE(("idn_resconf_setauxidnconverter()\n"));
565
566	if (ctx->aux_idn_converter != NULL)
567		idn_converter_destroy(ctx->aux_idn_converter);
568	ctx->aux_idn_converter = aux_idn_converter;
569	if (aux_idn_converter != NULL)
570		idn_converter_incrref(ctx->aux_idn_converter);
571}
572
573void
574idn_resconf_setlocalconverter(idn_resconf_t ctx,
575			      idn_converter_t local_converter) {
576#ifndef WITHOUT_ICONV
577	assert(ctx != NULL);
578
579	TRACE(("idn_resconf_setlocalconverter()\n"));
580
581	if (ctx->local_converter != NULL) {
582		idn_converter_destroy(ctx->local_converter);
583		ctx->local_converter = NULL;
584	}
585
586	if (local_converter == NULL)
587		ctx->local_converter_is_static = 0;
588	else {
589		ctx->local_converter = local_converter;
590		idn_converter_incrref(local_converter);
591		ctx->local_converter_is_static = 1;
592	}
593#endif /* WITHOUT_ICONV */
594}
595
596void
597idn_resconf_setlocalmapselector(idn_resconf_t ctx,
598				idn_mapselector_t local_mapper) {
599	assert(ctx != NULL);
600
601	TRACE(("idn_resconf_setlocalmapselector()\n"));
602
603	if (ctx->local_mapper != NULL)
604		idn_mapselector_destroy(ctx->local_mapper);
605	ctx->local_mapper = local_mapper;
606	if (local_mapper != NULL)
607		idn_mapselector_incrref(ctx->local_mapper);
608}
609
610void
611idn_resconf_setmapper(idn_resconf_t ctx, idn_mapper_t mapper) {
612	assert(ctx != NULL);
613
614	TRACE(("idn_resconf_setmapper()\n"));
615
616	if (ctx->mapper != NULL)
617		idn_mapper_destroy(ctx->mapper);
618	ctx->mapper = mapper;
619	if (mapper != NULL)
620		idn_mapper_incrref(ctx->mapper);
621}
622
623void
624idn_resconf_setnormalizer(idn_resconf_t ctx, idn_normalizer_t normalizer) {
625	assert(ctx != NULL);
626
627	TRACE(("idn_resconf_setnormalizer()\n"));
628
629	if (ctx->normalizer != NULL)
630		idn_normalizer_destroy(ctx->normalizer);
631	ctx->normalizer = normalizer;
632	if (normalizer != NULL)
633		idn_normalizer_incrref(ctx->normalizer);
634}
635
636void
637idn_resconf_setprohibitchecker(idn_resconf_t ctx,
638			       idn_checker_t prohibit_checker) {
639	assert(ctx != NULL);
640
641	TRACE(("idn_resconf_setprohibitchecker()\n"));
642
643	if (ctx->prohibit_checker != NULL)
644		idn_checker_destroy(ctx->prohibit_checker);
645	ctx->prohibit_checker = prohibit_checker;
646	if (prohibit_checker != NULL)
647		idn_checker_incrref(ctx->prohibit_checker);
648}
649
650void
651idn_resconf_setunassignedchecker(idn_resconf_t ctx,
652				 idn_checker_t unassigned_checker) {
653	assert(ctx != NULL);
654
655	TRACE(("idn_resconf_setunassignedchecker()\n"));
656
657	if (ctx->unassigned_checker != NULL)
658		idn_checker_destroy(ctx->unassigned_checker);
659	ctx->unassigned_checker = unassigned_checker;
660	if (unassigned_checker != NULL)
661		idn_checker_incrref(ctx->unassigned_checker);
662}
663
664void
665idn_resconf_setbidichecker(idn_resconf_t ctx,
666			   idn_checker_t bidi_checker) {
667	assert(ctx != NULL);
668
669	TRACE(("idn_resconf_setbidichecker()\n"));
670
671	if (ctx->bidi_checker != NULL)
672		idn_checker_destroy(ctx->bidi_checker);
673	ctx->bidi_checker = bidi_checker;
674	if (bidi_checker != NULL)
675		idn_checker_incrref(ctx->bidi_checker);
676}
677
678idn_result_t
679idn_resconf_setnameprepversion(idn_resconf_t ctx, const char *version)
680{
681	char prohibit_scheme_name[MAX_CONF_LINE_LENGTH + 1];
682	char unassigned_scheme_name[MAX_CONF_LINE_LENGTH + 1];
683	char bidi_scheme_name[MAX_CONF_LINE_LENGTH + 1];
684	idn_mapper_t mapper = NULL;
685	idn_normalizer_t normalizer = NULL;
686	idn_checker_t prohibit_checker = NULL;
687	idn_checker_t unassigned_checker = NULL;
688	idn_checker_t bidi_checker = NULL;
689	idn_result_t r;
690
691	assert(ctx != NULL && version != NULL);
692
693	TRACE(("idn_resconf_setnameprepversion()\n"));
694
695	/*
696	 * Set canonical scheme names.
697	 */
698	if (strlen(version) + strlen(IDN_CHECKER_PROHIBIT_PREFIX)
699	    > MAX_CONF_LINE_LENGTH) {
700		r = idn_invalid_name;
701		goto failure;
702	}
703	sprintf(prohibit_scheme_name, "%s%s",
704	        IDN_CHECKER_PROHIBIT_PREFIX, version);
705
706	if (strlen(version) + strlen(IDN_CHECKER_UNASSIGNED_PREFIX)
707	    > MAX_CONF_LINE_LENGTH) {
708		r = idn_invalid_name;
709		goto failure;
710	}
711	sprintf(unassigned_scheme_name, "%s%s",
712	        IDN_CHECKER_UNASSIGNED_PREFIX, version);
713
714	if (strlen(version) + strlen(IDN_CHECKER_BIDI_PREFIX)
715	    > MAX_CONF_LINE_LENGTH) {
716		r = idn_invalid_name;
717		goto failure;
718	}
719	sprintf(bidi_scheme_name, "%s%s",
720	        IDN_CHECKER_BIDI_PREFIX, version);
721
722	/*
723	 * Create objects.
724	 */
725	r = idn_mapper_create(&mapper);
726	if (r != idn_success)
727		goto failure;
728	r = idn_normalizer_create(&normalizer);
729	if (r != idn_success)
730		goto failure;
731	r = idn_checker_create(&prohibit_checker);
732	if (r != idn_success)
733		goto failure;
734	r = idn_checker_create(&unassigned_checker);
735	if (r != idn_success)
736		goto failure;
737	r = idn_checker_create(&bidi_checker);
738	if (r != idn_success)
739		goto failure;
740
741	r = idn_mapper_add(mapper, version);
742	if (r != idn_success)
743		goto failure;
744	r = idn_normalizer_add(normalizer, version);
745	if (r != idn_success)
746		goto failure;
747	r = idn_checker_add(prohibit_checker, prohibit_scheme_name);
748	if (r != idn_success)
749		goto failure;
750	r = idn_checker_add(unassigned_checker, unassigned_scheme_name);
751	if (r != idn_success)
752		goto failure;
753	r = idn_checker_add(bidi_checker, bidi_scheme_name);
754	if (r != idn_success)
755		goto failure;
756
757	/*
758	 * Set the objects.
759	 */
760	idn_resconf_setmapper(ctx, mapper);
761	idn_resconf_setnormalizer(ctx, normalizer);
762	idn_resconf_setprohibitchecker(ctx, prohibit_checker);
763	idn_resconf_setunassignedchecker(ctx, unassigned_checker);
764	idn_resconf_setbidichecker(ctx, bidi_checker);
765
766	/*
767	 * Destroy the objects.
768	 */
769	idn_mapper_destroy(mapper);
770	idn_normalizer_destroy(normalizer);
771	idn_checker_destroy(prohibit_checker);
772	idn_checker_destroy(unassigned_checker);
773	idn_checker_destroy(bidi_checker);
774
775	return (idn_success);
776
777failure:
778	if (mapper != NULL)
779		idn_mapper_destroy(mapper);
780	if (normalizer != NULL)
781		idn_normalizer_destroy(normalizer);
782	if (prohibit_checker != NULL)
783		idn_checker_destroy(prohibit_checker);
784	if (unassigned_checker != NULL)
785		idn_checker_destroy(unassigned_checker);
786	if (bidi_checker != NULL)
787		idn_checker_destroy(bidi_checker);
788
789	return (r);
790}
791
792idn_result_t
793idn_resconf_setalternateconvertername(idn_resconf_t ctx, const char *name,
794				      int flags) {
795	assert(ctx != NULL && name != NULL);
796
797	TRACE(("idn_resconf_setalternateconvertername(name=%s, flags=%d)\n",
798	      name, flags));
799
800	return (idn_success);
801}
802
803idn_result_t
804idn_resconf_setidnconvertername(idn_resconf_t ctx, const char *name,
805				int flags) {
806	idn_converter_t idn_converter;
807	idn_result_t r;
808
809	assert(ctx != NULL && name != NULL);
810
811	TRACE(("idn_resconf_setidnconvertername(name=%s, flags=%d)\n",
812	      name, flags));
813
814	r = idn_converter_create(name, &idn_converter, flags);
815	if (r != idn_success)
816		return (r);
817
818	if (ctx->idn_converter != NULL)
819		idn_converter_destroy(ctx->idn_converter);
820	ctx->idn_converter = idn_converter;
821
822	return (idn_success);
823}
824
825idn_result_t
826idn_resconf_setauxidnconvertername(idn_resconf_t ctx, const char *name,
827				    int flags) {
828	idn_converter_t aux_idn_converter;
829	const char *old_name;
830	idn_result_t r;
831
832	assert(ctx != NULL && name != NULL);
833
834	TRACE(("idn_resconf_setauxidnconvertername(name=%s, flags=%d)\n",
835	      name, flags));
836
837	if (ctx->aux_idn_converter != NULL) {
838	    old_name = idn_converter_localencoding(ctx->aux_idn_converter);
839	    if (old_name != NULL && strcmp(old_name, name) == 0)
840		return (idn_success);
841	}
842
843	r = idn_converter_create(name, &aux_idn_converter, flags);
844	if (r != idn_success)
845		return (r);
846
847	if (ctx->aux_idn_converter != NULL)
848		idn_converter_destroy(ctx->aux_idn_converter);
849	ctx->aux_idn_converter = aux_idn_converter;
850
851	return (idn_success);
852}
853
854idn_result_t
855idn_resconf_setlocalconvertername(idn_resconf_t ctx, const char *name,
856				  int flags) {
857#ifdef WITHOUT_ICONV
858	return idn_failure;
859
860#else /* WITHOUT_ICONV */
861	idn_converter_t local_converter;
862	idn_result_t r;
863
864	assert(ctx != NULL);
865
866	TRACE(("idn_resconf_setlocalconvertername(name=%s, flags=%d)\n",
867	      name == NULL ? "<null>" : name, flags));
868
869	if (ctx->local_converter != NULL) {
870		idn_converter_destroy(ctx->local_converter);
871		ctx->local_converter = NULL;
872	}
873	ctx->local_converter_is_static = 0;
874
875	if (name != NULL) {
876		r = idn_converter_create(name, &local_converter, flags);
877		if (r != idn_success)
878			return (r);
879		ctx->local_converter = local_converter;
880		ctx->local_converter_is_static = 1;
881	}
882
883	return (idn_success);
884
885#endif /* WITHOUT_ICONV */
886}
887
888idn_result_t
889idn_resconf_addalldelimitermapucs(idn_resconf_t ctx, unsigned long *v,
890				  int nv) {
891	idn_result_t r;
892
893	TRACE(("idn_resconf_addalldelimitermapucs(nv=%d)\n", nv));
894
895	if (ctx->delimiter_mapper == NULL) {
896		r = idn_delimitermap_create(&(ctx->delimiter_mapper));
897		if (r != idn_success)
898			return (r);
899	}
900
901	r = idn_delimitermap_addall(ctx->delimiter_mapper, v, nv);
902	return (r);
903}
904
905idn_result_t
906idn_resconf_addalllocalmapselectornames(idn_resconf_t ctx, const char *tld,
907					const char **names, int nnames) {
908	idn_result_t r;
909
910	assert(ctx != NULL && names != NULL && tld != NULL);
911
912	TRACE(("idn_resconf_addalllocalmapselectorname(tld=%s, nnames=%d)\n",
913	      tld, nnames));
914
915	if (ctx->local_mapper == NULL) {
916		r = idn_mapselector_create(&(ctx->local_mapper));
917		if (r != idn_success)
918			return (r);
919	}
920
921	r = idn_mapselector_addall(ctx->local_mapper, tld, names, nnames);
922	return (r);
923}
924
925idn_result_t
926idn_resconf_addallmappernames(idn_resconf_t ctx, const char **names,
927			      int nnames) {
928	idn_result_t r;
929
930	assert(ctx != NULL && names != NULL);
931
932	TRACE(("idn_resconf_addallmappername()\n"));
933
934	if (ctx->mapper == NULL) {
935		r = idn_mapper_create(&(ctx->mapper));
936		if (r != idn_success)
937			return (r);
938	}
939
940	r = idn_mapper_addall(ctx->mapper, names, nnames);
941	return (r);
942}
943
944idn_result_t
945idn_resconf_addallnormalizernames(idn_resconf_t ctx, const char **names,
946				  int nnames) {
947	idn_result_t r;
948
949	assert(ctx != NULL && names != NULL);
950
951	TRACE(("idn_resconf_addallnormalizername(nnames=%d)\n", nnames));
952
953	if (ctx->normalizer == NULL) {
954		r = idn_normalizer_create(&(ctx->normalizer));
955		if (r != idn_success)
956			return (r);
957	}
958
959	r = idn_normalizer_addall(ctx->normalizer, names, nnames);
960	return (r);
961}
962
963idn_result_t
964idn_resconf_addallprohibitcheckernames(idn_resconf_t ctx, const char **names,
965				       int nnames) {
966	char long_name[MAX_CONF_LINE_LENGTH + 1];
967	idn_result_t r;
968	int i;
969
970	assert(ctx != NULL && names != NULL);
971
972	TRACE(("idn_resconf_addallprohibitcheckername(nnames=%d)\n", nnames));
973
974	if (ctx->prohibit_checker == NULL) {
975		r = idn_checker_create(&(ctx->prohibit_checker));
976		if (r != idn_success)
977			return (r);
978	}
979
980	for (i = 0; i < nnames; i++, names++) {
981		if (strlen(*names) + strlen(IDN_CHECKER_PROHIBIT_PREFIX)
982			> MAX_CONF_LINE_LENGTH) {
983			return (idn_invalid_name);
984		}
985		strcpy(long_name, IDN_CHECKER_PROHIBIT_PREFIX);
986		strcat(long_name, *names);
987
988		r = idn_checker_add(ctx->prohibit_checker, long_name);
989		if (r != idn_success)
990			return (r);
991	}
992
993	return (idn_success);
994}
995
996idn_result_t
997idn_resconf_addallunassignedcheckernames(idn_resconf_t ctx, const char **names,
998					 int nnames) {
999	char long_name[MAX_CONF_LINE_LENGTH + 1];
1000	idn_result_t r;
1001	int i;
1002
1003	assert(ctx != NULL && names != NULL);
1004
1005	TRACE(("idn_resconf_addallunassignedcheckername(nnames=%d)\n",
1006	      nnames));
1007
1008	if (ctx->unassigned_checker == NULL) {
1009		r = idn_checker_create(&(ctx->unassigned_checker));
1010		if (r != idn_success)
1011			return (r);
1012	}
1013
1014	for (i = 0; i < nnames; i++, names++) {
1015		if (strlen(*names) + strlen(IDN_CHECKER_UNASSIGNED_PREFIX)
1016			> MAX_CONF_LINE_LENGTH) {
1017			return (idn_invalid_name);
1018		}
1019		strcpy(long_name, IDN_CHECKER_UNASSIGNED_PREFIX);
1020		strcat(long_name, *names);
1021
1022		r = idn_checker_add(ctx->unassigned_checker, long_name);
1023		if (r != idn_success)
1024			return (r);
1025	}
1026
1027	return (idn_success);
1028}
1029
1030idn_result_t
1031idn_resconf_addallbidicheckernames(idn_resconf_t ctx, const char **names,
1032				   int nnames) {
1033	char long_name[MAX_CONF_LINE_LENGTH + 1];
1034	idn_result_t r;
1035	int i;
1036
1037	assert(ctx != NULL && names != NULL);
1038
1039	TRACE(("idn_resconf_addallbidicheckername(nnames=%d)\n", nnames));
1040
1041	if (ctx->bidi_checker == NULL) {
1042		r = idn_checker_create(&(ctx->bidi_checker));
1043		if (r != idn_success)
1044			return (r);
1045	}
1046
1047	for (i = 0; i < nnames; i++, names++) {
1048		if (strlen(*names) + strlen(IDN_CHECKER_BIDI_PREFIX)
1049			> MAX_CONF_LINE_LENGTH) {
1050			return (idn_invalid_name);
1051		}
1052		strcpy(long_name, IDN_CHECKER_BIDI_PREFIX);
1053		strcat(long_name, *names);
1054
1055		r = idn_checker_add(ctx->bidi_checker, long_name);
1056		if (r != idn_success)
1057			return (r);
1058	}
1059
1060	return (idn_success);
1061}
1062
1063static idn_result_t
1064parse_conf(idn_resconf_t ctx, FILE *fp) {
1065	char line[MAX_CONF_LINE_LENGTH + 1];
1066	int lineno = 0;
1067	char *argv[3];
1068	int argc;
1069	idn_result_t r;
1070	int conf_mask = 0;
1071
1072	TRACE(("parse_conf()\n"));
1073
1074	/*
1075	 * Parse config file.  parsing of 'idn-encoding' line is
1076	 * postponed because 'alias-file' line must be processed
1077	 * before them.
1078	 */
1079	while (fgets(line, sizeof(line), fp) != NULL) {
1080		char *newline;
1081
1082		lineno++;
1083		newline = strpbrk(line, "\r\n");
1084		if (newline != NULL)
1085			*newline = '\0';
1086		else if (fgetc(fp) != EOF) {
1087			ERROR(("libidnkit: too long line \"%-.30s\", "
1088			       "line %d\n", line, lineno));
1089			return (idn_invalid_syntax);
1090		}
1091
1092		argc = split_args(line, argv, 2);
1093		if (argc == -1) {
1094			ERROR(("libidnkit: syntax error, line %d\n", lineno));
1095			return (idn_invalid_syntax);
1096		} else if (argc == 0 || argv[0][0] == '#') {
1097			continue;
1098		} else if (argc == 1) {
1099			ERROR(("libidnkit: syntax error, line %d\n", lineno));
1100			return (idn_invalid_syntax);
1101		}
1102
1103		if (strcmp(argv[0], "idn-encoding") == 0) {
1104			if (conf_mask & DEFAULT_CONF_IDN_ENCODING) {
1105				ERROR(("libidnkit: \"%s\" redefined, "
1106				       "line %d\n", argv[0], lineno));
1107				r = idn_invalid_syntax;
1108			} else {
1109				conf_mask |= DEFAULT_CONF_IDN_ENCODING;
1110				r = parse_idn_encoding(ctx, argv[1], lineno);
1111			}
1112		} else if (strcmp(argv[0], "local-map") == 0) {
1113			r = parse_local_map(ctx, argv[1], lineno);
1114
1115		} else if (strcmp(argv[0], "nameprep") == 0) {
1116			if (conf_mask & DEFAULT_CONF_NAMEPREP) {
1117				ERROR(("libidnkit: \"%s\" redefined, "
1118				       "line %d\n", argv[0], lineno));
1119				r = idn_invalid_syntax;
1120			} else {
1121				conf_mask |= DEFAULT_CONF_NAMEPREP;
1122				r = parse_nameprep(ctx, argv[1], lineno);
1123			}
1124		} else if (strcmp(argv[0], "nameprep-map") == 0 ||
1125			   strcmp(argv[0], "nameprep-normalize") == 0 ||
1126			   strcmp(argv[0], "nameprep-prohibit") == 0 ||
1127			   strcmp(argv[0], "nameprep-unassigned") == 0 ||
1128			   strcmp(argv[0], "alias-file") == 0 ||
1129			   strcmp(argv[0], "encoding-alias-file") == 0 ||
1130			   strcmp(argv[0], "normalize") == 0 ||
1131			   strcmp(argv[0], "server-encoding") == 0 ||
1132		           strcmp(argv[0], "alternate-encoding") == 0 ||
1133			   strcmp(argv[0], "delimiter-map") == 0) {
1134			WARNING(("libidnkit: obsolete command \"%s\", line %d "
1135			         "(ignored)\n", argv[0], lineno));
1136			r = idn_success;
1137		} else {
1138			ERROR(("libidnkit: unknown command \"%-.30s\", "
1139			       "line %d\n", argv[0], lineno));
1140			r = idn_invalid_syntax;
1141		}
1142		if (r != idn_success)
1143			return (r);
1144	}
1145
1146	lineno++;
1147
1148	if (conf_mask != DEFAULT_CONF_ALL) {
1149		return setdefaults_body(ctx, conf_mask);
1150	}
1151
1152	return (idn_success);
1153}
1154
1155static idn_result_t
1156parse_idn_encoding(idn_resconf_t ctx, char *args, int lineno) {
1157	idn_result_t r;
1158	char *argv[MAX_CONF_LINE_ARGS + 1];
1159	int argc;
1160
1161	argc = split_args(args, argv, MAX_CONF_LINE_ARGS + 1);
1162
1163	if (argc != 1) {
1164		ERROR(("libidnkit: wrong # of args for idn-encoding, "
1165		       "line %d\n", lineno));
1166		return (idn_invalid_syntax);
1167	}
1168
1169	r = idn_converter_create(argv[0], &ctx->idn_converter,
1170				 IDN_CONVERTER_DELAYEDOPEN |
1171				 IDN_CONVERTER_RTCHECK);
1172	if (r != idn_success) {
1173		ERROR(("libidnkit: cannot create idn converter, %s, "
1174		       "line %d\n", idn_result_tostring(r), lineno));
1175	}
1176
1177	return (r);
1178}
1179
1180static idn_result_t
1181parse_local_map(idn_resconf_t ctx, char *args, int lineno) {
1182	idn_result_t r;
1183	char *argv[MAX_CONF_LINE_ARGS + 1];
1184	int argc;
1185	int i;
1186
1187	argc = split_args(args, argv, MAX_CONF_LINE_ARGS + 1);
1188
1189	if (argc < 2 || argc > MAX_CONF_LINE_ARGS) {
1190		ERROR(("libidnkit: wrong # of args for local-map, line %d\n",
1191		       lineno));
1192		return (idn_invalid_syntax);
1193	}
1194
1195	if (ctx->local_mapper == NULL) {
1196		r = idn_mapselector_create(&ctx->local_mapper);
1197		if (r != idn_success) {
1198			ERROR(("libidnkit: cannot create local mapper, %s, "
1199			       "line %d\n", idn_result_tostring(r), lineno));
1200			return (r);
1201		}
1202	}
1203
1204	for (i = 1; i < argc; i++) {
1205		r = idn_mapselector_add(ctx->local_mapper, argv[0], argv[i]);
1206		if (r == idn_invalid_name) {
1207			ERROR(("libidnkit: map scheme unavailable \"%-.30s\""
1208			       " or invalid TLD \"%-.30s\", line %d\n",
1209			       argv[i], argv[0], lineno));
1210			return (r);
1211		} else if (r != idn_success) {
1212			return (r);
1213		}
1214	}
1215
1216	return (idn_success);
1217}
1218
1219static idn_result_t
1220parse_nameprep(idn_resconf_t ctx, char *args, int lineno) {
1221	idn_result_t r;
1222	char *argv[MAX_CONF_LINE_ARGS + 1];
1223	char scheme_name[MAX_CONF_LINE_LENGTH + 1];
1224	int argc;
1225
1226	argc = split_args(args, argv, MAX_CONF_LINE_ARGS + 1);
1227
1228	if (argc != 1) {
1229		ERROR(("libidnkit: wrong # of args for nameprep, line %d\n",
1230		       lineno));
1231		return (idn_invalid_syntax);
1232	}
1233
1234	/*
1235	 * Set mapper.
1236	 */
1237	r = idn_mapper_create(&ctx->mapper);
1238	if (r != idn_success) {
1239		ERROR(("libidnkit: cannot create mapper, %s, line %d\n",
1240		       idn_result_tostring(r), lineno));
1241		return (r);
1242	}
1243
1244	r = idn_mapper_add(ctx->mapper, argv[0]);
1245	if (r == idn_invalid_name) {
1246		ERROR(("libidnkit: map scheme unavailable \"%-.30s\", "
1247		       "line %d\n", argv[0], lineno));
1248		return (r);
1249	} else if (r != idn_success) {
1250		return (r);
1251	}
1252
1253	/*
1254	 * Set normalizer.
1255	 */
1256	r = idn_normalizer_create(&ctx->normalizer);
1257	if (r != idn_success) {
1258		ERROR(("libidnkit: cannot create normalizer, %s, line %d\n",
1259		       idn_result_tostring(r), lineno));
1260		return (r);
1261	}
1262
1263	r = idn_normalizer_add(ctx->normalizer, argv[0]);
1264	if (r == idn_invalid_name) {
1265		ERROR(("libidnkit: unknown normalization scheme \"%-.30s\", "
1266		       "line %d\n", argv[0], lineno));
1267		return (r);
1268	} else if (r != idn_success) {
1269		return (r);
1270	}
1271
1272	/*
1273	 * Set prohibit checker.
1274	 */
1275	r = idn_checker_create(&ctx->prohibit_checker);
1276	if (r != idn_success) {
1277		ERROR(("libidnkit: cannot create prohibit checker, %s, "
1278		       "line %d\n", idn_result_tostring(r), lineno));
1279		return (r);
1280	}
1281
1282	sprintf(scheme_name, "%s%s", IDN_CHECKER_PROHIBIT_PREFIX, argv[0]);
1283	r = idn_checker_add(ctx->prohibit_checker, scheme_name);
1284	if (r == idn_invalid_name) {
1285		ERROR(("libidnkit: unknown prohibit scheme \"%-.30s\", "
1286		       "line %d\n", argv[0], lineno));
1287		return (r);
1288	} else if (r != idn_success) {
1289		return (r);
1290	}
1291
1292	/*
1293	 * Set unassigned checker.
1294	 */
1295	r = idn_checker_create(&ctx->unassigned_checker);
1296	if (r != idn_success) {
1297		ERROR(("libidnkit: cannot create unassigned checker, %s, "
1298		       "line %d\n", idn_result_tostring(r), lineno));
1299		return (r);
1300	}
1301
1302	sprintf(scheme_name, "%s%s", IDN_CHECKER_UNASSIGNED_PREFIX, argv[0]);
1303	r = idn_checker_add(ctx->unassigned_checker, scheme_name);
1304	if (r == idn_invalid_name) {
1305		ERROR(("libidnkit: unknown unassigned scheme \"%-.30s\", "
1306		       "line %d\n", argv[0], lineno));
1307		return (r);
1308	} else if (r != idn_success) {
1309		return (r);
1310	}
1311
1312	/*
1313	 * Set bidi checker.
1314	 */
1315	r = idn_checker_create(&ctx->bidi_checker);
1316	if (r != idn_success) {
1317		ERROR(("libidnkit: cannot create bidi checker, %s, line %d\n",
1318		       idn_result_tostring(r), lineno));
1319		return (r);
1320	}
1321
1322	sprintf(scheme_name, "%s%s", IDN_CHECKER_BIDI_PREFIX, argv[0]);
1323	r = idn_checker_add(ctx->bidi_checker, scheme_name);
1324	if (r == idn_invalid_name) {
1325		ERROR(("libidnkit: unknown bidi scheme \"%-.30s\", "
1326		       "line %d\n", argv[0], lineno));
1327		return (r);
1328	} else if (r != idn_success) {
1329		return (r);
1330	}
1331
1332	return (idn_success);
1333}
1334
1335static int
1336split_args(char *s, char **av, int max_ac) {
1337	int ac;
1338	int i;
1339
1340	for (ac = 0; *s != '\0' && ac < max_ac; ac++) {
1341		if (ac > 0)
1342			*s++ = '\0';
1343		while (isspace((unsigned char)*s))
1344			s++;
1345		if (*s == '\0')
1346			break;
1347		if (*s == '"' || *s == '\'') {
1348			int qc = *s++;
1349			av[ac] = s;
1350			while (*s != qc) {
1351				if (*s == '\0')
1352					return (-1);
1353				s++;
1354			}
1355		} else {
1356			av[ac] = s;
1357			while (*s != '\0' && !isspace((unsigned char)*s))
1358				s++;
1359		}
1360	}
1361
1362	for (i = ac; i < max_ac; i++)
1363		av[i] = NULL;
1364
1365	return (ac);
1366}
1367
1368static void
1369resetconf(idn_resconf_t ctx) {
1370#ifndef WITHOUT_ICONV
1371	idn_resconf_setlocalconverter(ctx, NULL);
1372#endif
1373	idn_resconf_setidnconverter(ctx, NULL);
1374	idn_resconf_setauxidnconverter(ctx, NULL);
1375	idn_resconf_setdelimitermap(ctx, NULL);
1376	idn_resconf_setlocalmapselector(ctx, NULL);
1377	idn_resconf_setmapper(ctx, NULL);
1378	idn_resconf_setnormalizer(ctx, NULL);
1379	idn_resconf_setprohibitchecker(ctx, NULL);
1380	idn_resconf_setunassignedchecker(ctx, NULL);
1381	idn_resconf_setbidichecker(ctx, NULL);
1382}
1383
1384#ifndef WITHOUT_ICONV
1385static idn_result_t
1386update_local_converter(idn_resconf_t ctx) {
1387	idn_result_t r;
1388	const char *old_encoding;
1389	const char *new_encoding;
1390
1391	/*
1392	 * We don't update local converter, if the converter is set
1393	 * by idn_resconf_setlocalconverter() or
1394	 * idn_resconf_setlocalconvertername().
1395	 */
1396	if (ctx->local_converter_is_static)
1397		return (idn_success);
1398
1399	/*
1400	 * Update the local converter if the local encoding is changed.
1401	 */
1402	old_encoding = (ctx->local_converter != NULL) ?
1403		       idn_converter_localencoding(ctx->local_converter) :
1404		       NULL;
1405	new_encoding = idn_localencoding_name();
1406	if (new_encoding == NULL) {
1407		ERROR(("cannot determine local codeset name\n"));
1408		return (idn_notfound);
1409	}
1410
1411	if (old_encoding != NULL &&
1412	    new_encoding != NULL &&
1413	    strcmp(old_encoding, new_encoding) == 0) {
1414		return (idn_success);
1415	}
1416
1417	if (ctx->local_converter != NULL) {
1418		idn_converter_destroy(ctx->local_converter);
1419		ctx->local_converter = NULL;
1420	}
1421
1422	r = idn_converter_create(new_encoding,
1423				 &ctx->local_converter,
1424				 IDN_CONVERTER_RTCHECK);
1425	return (r);
1426}
1427#endif
1428
1429idn_result_t
1430idn_resconf_setdefaults(idn_resconf_t ctx)
1431{
1432	idn_result_t r;
1433
1434	assert(ctx != NULL);
1435
1436	TRACE(("idn_resconf_setdefaults()\n"));
1437
1438	resetconf(ctx);
1439	r = idn_delimitermap_create(&ctx->delimiter_mapper);
1440	if (r != idn_success) {
1441		ERROR(("libidnkit: cannot create delimiter mapper, %s\n",
1442		       idn_result_tostring(r)));
1443		return (r);
1444	}
1445
1446	return setdefaults_body(ctx, 0);
1447}
1448
1449static idn_result_t
1450setdefaults_body(idn_resconf_t ctx, int conf_mask) {
1451	idn_result_t r;
1452
1453	TRACE(("setdefaults_body()\n"));
1454	assert(ctx != NULL);
1455
1456	if (!(conf_mask & DEFAULT_CONF_NAMEPREP)) {
1457		TRACE(("set default nameprep\n"));
1458		r = idn_resconf_setnameprepversion(ctx, IDN_NAMEPREP_CURRENT);
1459		if (r != idn_success) {
1460			return (r);
1461		}
1462	}
1463	if (!(conf_mask & DEFAULT_CONF_IDN_ENCODING)) {
1464		TRACE(("set default idn encoding\n"));
1465		r = idn_converter_create(IDN_ENCODING_CURRENT,
1466					 &ctx->idn_converter,
1467					 IDN_CONVERTER_DELAYEDOPEN |
1468					 IDN_CONVERTER_RTCHECK);
1469		if (r != idn_success) {
1470			ERROR(("libidnkit: cannot create idn converter, %s\n",
1471			       idn_result_tostring(r)));
1472			return (r);
1473		}
1474	}
1475
1476	return (idn_success);
1477}
1478