1219019Sgabor/*-
2219019Sgabor * Copyright (C) 2009 Gabor Kovesdan <gabor@FreeBSD.org>
3219019Sgabor * All rights reserved.
4219019Sgabor *
5219019Sgabor * Redistribution and use in source and binary forms, with or without
6219019Sgabor * modification, are permitted provided that the following conditions
7219019Sgabor * are met:
8219019Sgabor * 1. Redistributions of source code must retain the above copyright
9219019Sgabor *    notice, this list of conditions and the following disclaimer.
10219019Sgabor * 2. Redistributions in binary form must reproduce the above copyright
11219019Sgabor *    notice, this list of conditions and the following disclaimer in the
12219019Sgabor *    documentation and/or other materials provided with the distribution.
13219019Sgabor *
14219019Sgabor * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15219019Sgabor * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16219019Sgabor * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17219019Sgabor * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18219019Sgabor * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19219019Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20219019Sgabor * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21219019Sgabor * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22219019Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23219019Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24219019Sgabor * SUCH DAMAGE.
25219019Sgabor */
26219019Sgabor
27219019Sgabor#include <sys/cdefs.h>
28219019Sgabor__FBSDID("$FreeBSD$");
29219019Sgabor
30219019Sgabor#include <sys/endian.h>
31219019Sgabor#include <sys/types.h>
32219019Sgabor
33219019Sgabor#include <err.h>
34219019Sgabor#include <errno.h>
35219019Sgabor#include <iconv.h>
36219019Sgabor#include <stdbool.h>
37219019Sgabor#include <stdio.h>
38219019Sgabor#include <stdlib.h>
39219019Sgabor
40219019Sgabor/*
41219019Sgabor * iconv_open must return (iconv_t)-1 on non-existing encoding
42219019Sgabor * and set errno to EINVAL.
43219019Sgabor */
44219019Sgaborstatic int
45219019Sgaboropen_1(void)
46219019Sgabor{
47219019Sgabor	iconv_t cd;
48219019Sgabor
49219019Sgabor	errno = 0;
50219019Sgabor	cd = iconv_open("nonexisting", "foobar");
51219019Sgabor
52219019Sgabor	if ((cd == (iconv_t)-1) && (errno == EINVAL))
53219019Sgabor		return (0);
54219019Sgabor	else {
55219019Sgabor		iconv_close(cd);
56219019Sgabor		return (1);
57219019Sgabor	}
58219019Sgabor}
59219019Sgabor
60219019Sgabor/*
61219019Sgabor * iconv_open must return (iconv_t)-1 if too much files are open
62219019Sgabor * and set errno to ENFILE.
63219019Sgabor */
64219019Sgabor#define	MAX_LIMIT	1025
65219019Sgaborstatic int
66219019Sgaboropen_2(void)
67219019Sgabor{
68219019Sgabor	iconv_t cd[MAX_LIMIT];
69219019Sgabor	int i, ret;
70219019Sgabor
71219019Sgabor	errno = 0;
72219019Sgabor	for (i = 0; i < MAX_LIMIT; i++) {
73219019Sgabor		cd[i] = iconv_open("ASCII", "UTF8");
74219019Sgabor		if (cd[i] == (iconv_t)-1)
75219019Sgabor			break;
76219019Sgabor	}
77219019Sgabor
78219019Sgabor	ret = (cd[i] == (iconv_t)-1) && ((errno == ENFILE) ||
79219019Sgabor	    (errno == EMFILE))  ? 0 : 1;
80219019Sgabor	for (; i > 0; i--)
81219019Sgabor		iconv_close(cd[i]);
82219019Sgabor	return (ret);
83219019Sgabor}
84219019Sgabor
85219019Sgabor/*
86219019Sgabor * iconv_close must return (iconv_t)-1 if conversion descriptor is
87219019Sgabor * invalid and set errno to EBADF.
88219019Sgabor */
89219019Sgaborstatic int
90219019Sgaborclose_1(void)
91219019Sgabor{
92219019Sgabor	iconv_t cd = (iconv_t)-1;
93219019Sgabor
94219019Sgabor	return ((iconv_close(cd) == -1) && (errno = EBADF) ? 0 : 1);
95219019Sgabor}
96219019Sgabor
97219019Sgaborstatic int
98219019Sgaborconv_ebadf(void)
99219019Sgabor{
100219019Sgabor	iconv_t	cd = (iconv_t)-1;
101219019Sgabor
102219019Sgabor	errno = 0;
103219019Sgabor	return ((iconv(cd, NULL, 0, NULL, 0) == (size_t)-1 && errno == EBADF) ? 0 : 1);
104219019Sgabor}
105219019Sgabor
106219019Sgaborstatic int
107219019Sgaborconv_ret(void)
108219019Sgabor{
109219019Sgabor	iconv_t cd;
110219019Sgabor	size_t inbytesleft, outbytesleft;
111219019Sgabor	const char *inptr;
112219019Sgabor	char *outptr;
113219019Sgabor	uint32_t outbuf[4];
114219019Sgabor	uint32_t inbuf[2] = { 0x00000151, 0x00000171 };
115219019Sgabor
116219019Sgabor	if ((cd = iconv_open("ASCII", "UTF-32LE")) == (iconv_t)-1)
117219019Sgabor		return (1);
118219019Sgabor
119219019Sgabor	inptr = (const char *)inbuf;
120219019Sgabor	outptr = (char *)outbuf;
121219019Sgabor	inbytesleft = 8;
122219019Sgabor	outbytesleft = 16;
123219019Sgabor
124219019Sgabor	return (iconv(cd, &inptr, &inbytesleft, &outptr, &outbytesleft) == 2 ? 0 : 1);
125219019Sgabor}
126219019Sgabor
127219019Sgaborstatic int
128219019Sgaborconv_2big(void)
129219019Sgabor{
130219019Sgabor	iconv_t cd;
131219019Sgabor	size_t inbytesleft, outbytesleft;
132219019Sgabor	const char *inptr;
133219019Sgabor	char *outptr;
134219019Sgabor	uint32_t inbuf[4];
135219019Sgabor	uint32_t outbuf[2];
136219019Sgabor	int ret;
137219019Sgabor
138219019Sgabor	if ((cd = iconv_open("ASCII", "ASCII")) == (iconv_t)-1)
139219019Sgabor		return (1);
140219019Sgabor
141219019Sgabor	inptr = (const char *)inbuf;
142219019Sgabor	outptr = (char *)outbuf;
143219019Sgabor	inbytesleft = 16;
144219019Sgabor	outbytesleft = 8;
145219019Sgabor
146219019Sgabor	errno = 0;
147219019Sgabor	ret = iconv(cd, &inptr, &inbytesleft, &outptr, &outbytesleft);
148219019Sgabor
149219019Sgabor#ifdef VERBOSE
150219019Sgabor	printf("inptr - inbuf = %d\n", (const uint8_t *)inptr - (uint8_t *)inbuf);
151219019Sgabor	printf("inbytesleft = %d\n", inbytesleft);
152219019Sgabor	printf("outbytesleft = %d\n", outbytesleft);
153219019Sgabor	printf("outptr - outbuf = %d\n", (uint8_t *)outptr - (uint8_t *)outbuf);
154219019Sgabor	printf("errno = %d\n", errno);
155219019Sgabor	printf("ret = %d\n", (int)ret);
156219019Sgabor#endif
157219019Sgabor
158219019Sgabor	if (((const uint8_t *)inptr - (uint8_t *)inbuf == 8) && (inbytesleft == 8)  &&
159219019Sgabor	    (outbytesleft == 0) && ((uint8_t *)outptr - (uint8_t *)outbuf == 8) &&
160219019Sgabor	    (errno == E2BIG) && ((size_t)ret == (size_t)-1))
161219019Sgabor		return (0);
162219019Sgabor	else
163219019Sgabor		return (1);
164219019Sgabor}
165219019Sgabor
166219019Sgaborstatic int
167219019Sgaborconv_einval(void)
168219019Sgabor{
169219019Sgabor	iconv_t	 cd;
170219019Sgabor	size_t inbytesleft, outbytesleft;
171219019Sgabor	const char *inptr;
172219019Sgabor	char *outptr;
173219019Sgabor	uint32_t outbuf[4];
174219019Sgabor        uint16_t inbuf[1] = { 0xEA42 };
175219019Sgabor	int ret;
176219019Sgabor
177219019Sgabor	if ((cd = iconv_open("UTF-32", "BIG5")) == (iconv_t)-1)
178219019Sgabor		return (1);
179219019Sgabor
180219019Sgabor	inptr = (const char *)inbuf;
181219019Sgabor	outptr = (char *)outbuf;
182219019Sgabor	inbytesleft = 2;
183219019Sgabor	outbytesleft = 16;
184219019Sgabor
185219019Sgabor	errno = 0;
186219019Sgabor	ret = iconv(cd, &inptr, &inbytesleft, &outptr, &outbytesleft);
187219019Sgabor
188219019Sgabor#ifdef VERBOSE
189219019Sgabor	printf("inptr - inbuf = %d\n", (const uint8_t *)inptr - (uint8_t *)inbuf);
190219019Sgabor	printf("inbytesleft = %d\n", inbytesleft);
191219019Sgabor	printf("outbytesleft = %d\n", outbytesleft);
192219019Sgabor	printf("outptr - outbuf = %d\n", (uint8_t *)outptr - (uint8_t *)outbuf);
193219019Sgabor	printf("errno = %d\n", errno);
194219019Sgabor	printf("ret = %d\n", (int)ret);
195219019Sgabor#endif
196219019Sgabor
197219019Sgabor	if (((const uint8_t *)inptr - (uint8_t *)inbuf == 1) && (inbytesleft == 1)  &&
198219019Sgabor	    (outbytesleft == 8) && ((uint8_t *)outptr - (uint8_t *)outbuf == 8) &&
199219019Sgabor	    (errno == EINVAL) && ((size_t)ret == (size_t)-1))
200219019Sgabor		return (0);
201219019Sgabor	else
202219019Sgabor		return (1);
203219019Sgabor}
204219019Sgabor
205219019Sgaborstatic int
206219019Sgaborconv_eilseq(void)
207219019Sgabor{
208219019Sgabor	iconv_t cd;
209219019Sgabor	size_t inbytesleft, outbytesleft;
210219019Sgabor	const char *inptr;
211219019Sgabor	char *outptr;
212219019Sgabor	uint32_t outbuf[4];
213219019Sgabor	uint16_t inbuf[1] = { 0x8AC0 };
214219019Sgabor	int ret;
215219019Sgabor
216219019Sgabor	if ((cd = iconv_open("Latin2", "UTF-16LE")) == (iconv_t)-1)
217219019Sgabor		return (1);
218219019Sgabor
219219019Sgabor	inptr = (const char *)inbuf;
220219019Sgabor	outptr = (char *)outbuf;
221219019Sgabor	inbytesleft = 4;
222219019Sgabor	outbytesleft = 16;
223219019Sgabor
224219019Sgabor	errno = 0;
225219019Sgabor	ret = iconv(cd, &inptr, &inbytesleft, &outptr, &outbytesleft);
226219019Sgabor
227219019Sgabor#ifdef VERBOSE
228219019Sgabor	printf("inptr - inbuf = %d\n", (const uint8_t *)inptr - (uint8_t *)inbuf);
229219019Sgabor	printf("inbytesleft = %d\n", inbytesleft);
230219019Sgabor	printf("outbytesleft = %d\n", outbytesleft);
231219019Sgabor	printf("outptr - outbuf = %d\n", (uint8_t *)outptr - (uint8_t *)outbuf);
232219019Sgabor	printf("errno = %d\n", errno);
233219019Sgabor	printf("ret = %d\n", (int)ret);
234219019Sgabor#endif
235219019Sgabor
236219019Sgabor	if (((const uint8_t *)inptr - (uint8_t *)inbuf == 0) && (inbytesleft == 4)  &&
237219019Sgabor	    (outbytesleft == 16) && ((uint8_t *)outptr - (uint8_t *)outbuf == 0) &&
238219019Sgabor	    (errno == EILSEQ) && ((size_t)ret == (size_t)-1))
239219019Sgabor		return (0);
240219019Sgabor	else
241219019Sgabor		return (1);
242219019Sgabor}
243219019Sgabor
244219019Sgaborstatic void
245219019Sgabortest(int (tester) (void), const char * label)
246219019Sgabor{
247219019Sgabor	int ret;
248219019Sgabor
249219019Sgabor	if ((ret = tester()))
250219019Sgabor		printf("%s failed (%d)\n", label, ret);
251219019Sgabor	else
252219019Sgabor		printf("%s succeeded\n", label);
253219019Sgabor}
254219019Sgabor
255219019Sgaborint
256219019Sgabormain(void)
257219019Sgabor{
258219019Sgabor
259219019Sgabor	test(open_1, "open_1");
260219019Sgabor	test(open_2, "open_2");
261219019Sgabor	test(close_1, "close_1");
262219019Sgabor	test(conv_ret, "conv_ret");
263219019Sgabor	test(conv_ebadf, "conv_ebadf");
264219019Sgabor	test(conv_2big, "conv_2big");
265219019Sgabor	test(conv_einval, "conv_einval");
266219019Sgabor	test(conv_eilseq, "conv_eilseq");
267219019Sgabor}
268