1/*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source.  A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12/*
13 * Copyright 2014 Garrett D'Amore <garrett@damore.org>
14 */
15
16/*
17 * This program tests that wcsrtombs and friends work properly.
18 * In order for it to work, it requires that some additional locales
19 * be installed.
20 */
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <locale.h>
26#include <wchar.h>
27#include <err.h>
28#include <errno.h>
29#include <unistd.h>
30#include <xlocale.h>
31#include <note.h>
32#include "test_common.h"
33
34int extra_debug = 0;
35
36#define	NUMTHR	300
37#define	NUMITR	300
38
39/*
40 * Note that this file is easiest edited with a UTF-8 capable editor,
41 * as there are embedded UTF-8 symbols in some of the strings.
42 */
43struct wcsrtombs_test {
44	char		mbs[32];
45	wchar_t		wcs[32];
46};
47
48#define	TESTING_MBS	"TESTING"
49#define	TESTING_WCS 	{ 'T', 'E', 'S', 'T', 'I', 'N', 'G', 0 }
50#define	HELLO_RU_MBS	"������������"
51#define	HELLO_RU_WCS	{ 1055, 1056, 1048, 1042, 1045, 1058, 0 }
52#define	HELLO_EN_MBS	"HELLO"
53#define	HELLO_EN_WCS	{ 'H', 'E', 'L', 'L', 'O', 0 }
54
55/* Unicode values never have the high order bit set */
56#define	BAD_WCS		{ 'B', 'A', 'D', (wchar_t)0xf000f000, 'W', 'C', 'S' }
57
58struct wcsrtombs_test C_data[] = {
59	{ TESTING_MBS,	TESTING_WCS },
60	{ HELLO_EN_MBS,	HELLO_EN_WCS },
61	{ 0, 0 },
62};
63
64struct wcsrtombs_test utf8_data[] = {
65	{ TESTING_MBS,	TESTING_WCS },
66	{ HELLO_EN_MBS,	HELLO_EN_WCS },
67	{ HELLO_RU_MBS,	HELLO_RU_WCS },
68	{ 0, 0 },
69};
70
71struct {
72	const char *locale;
73	struct wcsrtombs_test *test;
74} locales[] =  {
75	{ "C",			C_data },
76	{ "en_US.UTF-8",	utf8_data },
77	{ NULL, 		NULL }
78};
79
80void
81test_wcsrtombs_1(const char *locale, struct wcsrtombs_test *test)
82{
83	test_t		t;
84	char 		*v;
85	mbstate_t	ms;
86
87	t = test_start("wcsrtombs (locale %s)", locale);
88
89	v = setlocale(LC_ALL, locale);
90	if (v == NULL) {
91		test_failed(t, "setlocale failed: %s", strerror(errno));
92	}
93	if (strcmp(v, locale) != 0) {
94		test_failed(t, "setlocale got %s instead of %s", v, locale);
95	}
96
97	for (int i = 0; test[i].mbs[0] != 0; i++) {
98		char mbs[32];
99		const wchar_t *wcs = test[i].wcs;
100		size_t cnt;
101
102		(void) memset(&ms, 0, sizeof (ms));
103		(void) memset(mbs, 0, sizeof (mbs));
104		cnt = wcsrtombs(mbs, &wcs, sizeof (mbs), &ms);
105		if (cnt != strlen(test[i].mbs)) {
106			test_failed(t, "incorrect return value: %d != %d",
107			    cnt, strlen(test[i].mbs));
108		}
109		if (strcmp(mbs, test[i].mbs) != 0) {
110			test_failed(t, "wrong result: %s != %s",
111			    mbs, test[i].mbs);
112		}
113		if (extra_debug) {
114			test_debugf(t, "mbs is %s", mbs);
115		}
116	}
117	test_passed(t);
118}
119
120void
121test_wcsrtombs_l(const char *locale, struct wcsrtombs_test *test)
122{
123	test_t	t;
124	locale_t loc;
125	char 	*v;
126	mbstate_t	ms;
127
128	t = test_start("wcsrtombs_l (locale %s)", locale);
129
130	v = setlocale(LC_ALL, "C");
131	if (v == NULL) {
132		test_failed(t, "setlocale failed: %s", strerror(errno));
133	}
134	if (strcmp(v, "C") != 0) {
135		test_failed(t, "setlocale got %s instead of %s", v, "C");
136	}
137
138	loc = newlocale(LC_ALL_MASK, locale, NULL);
139	if (loc == NULL) {
140		test_failed(t, "newlocale failed: %s", strerror(errno));
141	}
142
143	for (int i = 0; test[i].mbs[0] != 0; i++) {
144		char mbs[32];
145		const wchar_t *wcs = test[i].wcs;
146		size_t cnt;
147
148		(void) memset(&ms, 0, sizeof (ms));
149		(void) memset(mbs, 0, sizeof (mbs));
150		cnt = wcsrtombs_l(mbs, &wcs, sizeof (mbs), &ms, loc);
151		if (cnt != strlen(test[i].mbs)) {
152			test_failed(t, "incorrect return value: %d != %d",
153			    cnt, strlen(test[i].mbs));
154		}
155		if (strcmp(mbs, test[i].mbs) != 0) {
156			test_failed(t, "wrong result: %s != %s", mbs,
157			    test[i].mbs);
158		}
159		if (extra_debug) {
160			test_debugf(t, "mbs is %s", mbs);
161		}
162	}
163	test_passed(t);
164}
165
166void
167test_wcsrtombs_thr_iter(test_t t, const char *locale,
168    struct wcsrtombs_test *test)
169{
170	locale_t loc;
171	mbstate_t	ms;
172
173	loc = newlocale(LC_ALL_MASK, locale, NULL);
174	if (loc == NULL) {
175		test_failed(t, "newlocale failed: %s", strerror(errno));
176	}
177
178	for (int i = 0; test[i].mbs[0] != 0; i++) {
179		char mbs[32];
180		const wchar_t *wcs = test[i].wcs;
181		size_t cnt;
182
183		(void) memset(&ms, 0, sizeof (ms));
184		(void) memset(mbs, 0, sizeof (mbs));
185		cnt = wcsrtombs_l(mbs, &wcs, sizeof (mbs), &ms, loc);
186		if (cnt != strlen(test[i].mbs)) {
187			test_failed(t, "incorrect return value: %d != %d",
188			    cnt, strlen(test[i].mbs));
189		}
190		if (strcmp(mbs, test[i].mbs) != 0) {
191			test_failed(t, "wrong result: %s != %s", mbs,
192			    test[i].mbs);
193		}
194		if (extra_debug) {
195			test_debugf(t, "mbs is %s", mbs);
196		}
197	}
198
199	freelocale(loc);
200}
201
202void
203test_wcsrtombs_thr_work(test_t t, void *arg)
204{
205	_NOTE(ARGUNUSED(arg));
206	for (int j = 0; j < NUMITR; j++) {
207		test_debugf(t, "iteration %d", j);
208		for (int i = 0; locales[i].locale != NULL; i++) {
209			test_wcsrtombs_thr_iter(t, locales[i].locale,
210			    locales[i].test);
211		}
212	}
213	test_passed(t);
214}
215
216void
217test_wcsrtombs_threaded(void)
218{
219	(void) setlocale(LC_ALL, "C");
220	test_run(NUMTHR, test_wcsrtombs_thr_work, NULL, "wcsrtombs_threaded");
221}
222
223void
224test_wcsrtombs_partial(void)
225{
226	test_t		t;
227	mbstate_t	ms;
228	wchar_t		src[32] = HELLO_RU_WCS;
229	char		mbs[32];
230	char		*dst;
231	const wchar_t	*wcs;
232	size_t 		cnt;
233
234
235	(void) memset(&ms, 0, sizeof (ms));
236	t = test_start("wcsrtombs_partial");
237
238	if (setlocale(LC_ALL, "ru_RU.UTF-8") == NULL) {
239		test_failed(t, "setlocale failed: %s", strerror(errno));
240	}
241
242	wcs = src;
243	dst = mbs;
244	cnt = wcsrtombs(dst, &wcs, 1, &ms);
245	if (cnt != 0) {
246		test_failed(t, "gave back a conversion cnt %d != 0", cnt);
247	}
248	if (wcs != src) {
249		test_failed(t, "incorrectly advanced wcs");
250	}
251
252	cnt = wcsrtombs(dst, &wcs, 2, &ms);
253	if (cnt != 2) {
254		test_failed(t, "gave back a conversion cnt %d != 2", cnt);
255	}
256	dst += cnt;
257
258	cnt = wcsrtombs(dst, &wcs, 4, &ms);
259	dst += cnt;
260
261	cnt = wcsrtombs(dst, &wcs, sizeof (mbs) - strlen(mbs), &ms);
262	if (extra_debug) {
263		test_debugf(t, "mbs is %s", mbs);
264	}
265	if (strcmp(mbs, HELLO_RU_MBS) != 0) {
266		test_failed(t, "wrong result: %s != %s", mbs, HELLO_RU_MBS);
267	}
268	test_passed(t);
269}
270
271void
272test_wcsrtombs_negative(void)
273{
274	mbstate_t	ms;
275	const wchar_t	*wcs;
276	char		mbs[32];
277	char		*dst;
278	int		e;
279	wchar_t		src[32] = BAD_WCS;
280	test_t		t;
281	int		cnt;
282
283	t = test_start("wcsrtombs_negative");
284
285	(void) memset(&ms, 0, sizeof (ms));
286	if (setlocale(LC_ALL, "ru_RU.UTF-8") == NULL) {
287		test_failed(t, "setlocale failed: %s", strerror(errno));
288	}
289
290	wcs = src;
291	dst = mbs;
292	cnt = wcsrtombs(dst, &wcs, sizeof (mbs), &ms);
293	if (cnt != -1) {
294		test_failed(t, "bogus success (%d)", cnt);
295	}
296	if ((e = errno) != EILSEQ) {
297		test_failed(t, "wrong errno, wanted %d (EILSEQ), got %d: %s",
298		    EILSEQ, e, strerror(e));
299	}
300	test_passed(t);
301}
302
303void
304test_wcsnrtombs_partial(void)
305{
306	test_t		t;
307	mbstate_t	ms;
308	wchar_t		src[32] = HELLO_RU_WCS;
309	char		mbs[32];
310	char		*dst;
311	const wchar_t	*wcs;
312	size_t 		cnt;
313
314
315	(void) memset(&ms, 0, sizeof (ms));
316	t = test_start("wcsrntombs_partial");
317
318	if (setlocale(LC_ALL, "ru_RU.UTF-8") == NULL) {
319		test_failed(t, "setlocale failed: %s", strerror(errno));
320	}
321
322	wcs = src;
323	dst = mbs;
324	cnt = wcsnrtombs(dst, &wcs, 1, 1, &ms);
325	if (cnt != 0) {
326		test_failed(t, "gave back a conversion cnt %d != 0", cnt);
327	}
328	if (wcs != src) {
329		test_failed(t, "incorrectly advanced wcs");
330	}
331
332	/* we should get just 2 wide characters (expanding to 4 bytes) */
333	cnt = wcsnrtombs(dst, &wcs, 2, sizeof (mbs), &ms);
334	if (cnt != 4) {
335		test_failed(t, "gave back a conversion cnt %d != 4", cnt);
336	}
337	dst += cnt;
338
339	cnt = wcsnrtombs(dst, &wcs, 32, sizeof (mbs) - strlen(mbs), &ms);
340	if (extra_debug) {
341		test_debugf(t, "mbs is %s", mbs);
342	}
343	if (strcmp(mbs, HELLO_RU_MBS) != 0) {
344		test_failed(t, "wrong result: %s != %s", mbs, HELLO_RU_MBS);
345	}
346	test_passed(t);
347}
348
349void
350test_wcsrtombs(void)
351{
352	for (int i = 0; locales[i].locale != NULL; i++) {
353		test_wcsrtombs_1(locales[i].locale, locales[i].test);
354		test_wcsrtombs_l(locales[i].locale, locales[i].test);
355	}
356}
357
358int
359main(int argc, char **argv)
360{
361	int optc;
362
363	while ((optc = getopt(argc, argv, "dfD")) != EOF) {
364		switch (optc) {
365		case 'd':
366			test_set_debug();
367			break;
368		case 'f':
369			test_set_force();
370			break;
371		case 'D':
372			test_set_debug();
373			extra_debug++;
374			break;
375		default:
376			(void) fprintf(stderr, "Usage: %s [-dfD]\n", argv[0]);
377			exit(1);
378		}
379	}
380
381	test_wcsrtombs();
382	test_wcsrtombs_partial();
383	test_wcsrtombs_negative();
384	test_wcsrtombs_threaded();
385	test_wcsnrtombs_partial();
386
387	exit(0);
388}
389