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 }