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 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 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 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 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 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 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 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 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 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 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