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 
34 int 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  */
43 struct 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 
58 struct wcsrtombs_test C_data[] = {
59 	{ TESTING_MBS,	TESTING_WCS },
60 	{ HELLO_EN_MBS,	HELLO_EN_WCS },
61 	{ 0, 0 },
62 };
63 
64 struct 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 
71 struct {
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 
80 void
test_wcsrtombs_1(const char * locale,struct wcsrtombs_test * test)81 test_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 
120 void
test_wcsrtombs_l(const char * locale,struct wcsrtombs_test * test)121 test_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 
166 void
test_wcsrtombs_thr_iter(test_t t,const char * locale,struct wcsrtombs_test * test)167 test_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 
202 void
test_wcsrtombs_thr_work(test_t t,void * arg)203 test_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 
216 void
test_wcsrtombs_threaded(void)217 test_wcsrtombs_threaded(void)
218 {
219 	(void) setlocale(LC_ALL, "C");
220 	test_run(NUMTHR, test_wcsrtombs_thr_work, NULL, "wcsrtombs_threaded");
221 }
222 
223 void
test_wcsrtombs_partial(void)224 test_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 
271 void
test_wcsrtombs_negative(void)272 test_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 
303 void
test_wcsnrtombs_partial(void)304 test_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 
349 void
test_wcsrtombs(void)350 test_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 
358 int
main(int argc,char ** argv)359 main(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