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