Ne.app NeANT
The Ne.app NeANT User module of the NeANT distribution.
locale.cpp
Go to the documentation of this file.
1
29// expand _TEST_EXPORT macros
30#define _RWSTD_TEST_SRC
31
32
33#include <rw_locale.h>
34#include <rw_environ.h> // for rw_putenv()
35#include <rw_file.h> // for SHELL_RM_RF, rw_tmpnam
36#include <rw_process.h> // for rw_system()
37#include <rw_printf.h> // for rw_snprintf()
38#include <rw_fnmatch.h> // for rw_fnmatch()
39#include <rw_braceexp.h> // for rw_shell_expand()
40#include <rw_driver.h> // for rw_error()
41
42#ifdef _RWSTD_OS_LINUX
43# ifdef _RWSTD_NO_PURE_C_HEADERS
44 // on Linux define _XOPEN_SOURCE to get CODESET defined in <langinfo.h>
45 // (avoid this hackery when using pure "C' headers (i.e., with the EDG
46 // eccp demo)
47# define _XOPEN_SOURCE 500 /* Single Unix conformance */
48 // bring __int32_t into scope (otherwise <wctype.h> fails to compile)
49# include <sys/types.h>
50# endif
51#endif // Linux
52
53#include <fcntl.h>
54#include <sys/stat.h> // for stat
55
56#ifndef _WIN32
57# include <unistd.h>
58# include <sys/wait.h> // for WIFEXITED(), WIFSIGNALED(), WTERMSIG()
59#else
60# include <io.h>
61# ifdef _MSC_VER
62# include <crtdbg.h> // for _malloc_dbg()
63# endif
64#endif
65
66#include <ios> // for ios::*
67#include <limits> // for numeric_limits
68#include <locale> // for money_base::pattern
69
70#include <assert.h> // for assert
71#include <limits.h> // for PATH_MAX
72#include <locale.h> // for LC_XXX macros, setlocale
73#include <stdarg.h> // for va_copy, va_list, ...
74#include <stdio.h> // for fgets, remove, sprintf, ...
75#include <stdlib.h> // for getenv, free, malloc, realloc
76#include <string.h> // for strcat, strcpy, strlen, ...
77#include <ctype.h>
78#include <wchar.h> // for wcslen, ...
79
80#ifndef _WIN32
81# include <clocale>
82# ifndef LC_MESSAGES
83# define LC_MESSAGES _RWSTD_LC_MESSAGES
84# endif // LC_MESSAGES
85# define EXE_SUFFIX ""
86#else // if Windows
87# define _RWSTD_NO_LANGINFO
88# define EXE_SUFFIX ".exe"
89#endif // _WIN32
90
91#ifndef _RWSTD_NO_LANGINFO
92# include <langinfo.h>
93#endif
94
95#if !defined (PATH_MAX) || PATH_MAX < 128 || 4096 < PATH_MAX
96 // deal with undefined, bogus, or excessive values
97# undef PATH_MAX
98# define PATH_MAX 1024
99#endif
100
101
102#define TOPDIR "TOPDIR" /* the TOPDIR environment variable */
103#define BINDIR "BINDIR" /* the BINDIR environment variable */
104
105
106#if _RWSTD_PATH_SEP == '/'
107# define SLASH "/"
108# define IS_ABSOLUTE_PATHNAME(path) (_RWSTD_PATH_SEP == *(path))
109#else
110# define SLASH "\\"
111# define IS_ABSOLUTE_PATHNAME(path) \
112 ( ( 'A' <= *(path) && 'Z' >= *(path) \
113 || 'a' <= *(path) && 'z' >= *(path)) \
114 && ':' == (path)[1] \
115 && _RWSTD_PATH_SEP == (path)[2])
116#endif
117
118// relative paths to the etc/nls directory and its subdirectories
119#define RELPATH "etc" SLASH "nls"
120#define TESTS_ETC_PATH "tests" SLASH "etc"
121
122// extension of the catalog file
123#ifndef _WIN32
124# define RW_CAT_EXT ".cat"
125#else
126# define RW_CAT_EXT ".dll"
127#endif
128
129
131_TEST_EXPORT int
132rw_locale (const char *args, const char *fname)
133{
134 // use BINDIR to determine the location of the locale command
135 const char* bindir = getenv (BINDIR);
136 if (!bindir)
137 bindir = ".." SLASH "bin";
138
139 int ret;
140
141 if (fname)
142 ret = rw_system ("%s%slocale%s %s",
143 bindir, SLASH, EXE_SUFFIX, args);
144 else
145 ret = rw_system ("%s%slocale%s %s >%s",
146 bindir, SLASH, EXE_SUFFIX, args, fname);
147
148 return ret;
149}
150
151
153_TEST_EXPORT const char*
154rw_localedef (const char *args,
155 const char* src, const char *charmap, const char *locname)
156{
157 assert (src && charmap);
158
159 // create a fully qualified pathname of the locale database
160 // when (locname == 0), the pathname is computed by appending
161 // the name of the character map file `charmap' to the name
162 // of the locale definition file `src'
163 // otherwise, when `locname' is not a pathname, the pathname
164 // of the locale database is formed by appending `locname'
165 // to the name of the locale root directory
166 static char locale_path [PATH_MAX];
167
168 const char* locale_root = getenv (LOCALE_ROOT_ENVAR);
169 if (!locale_root)
170 locale_root = ".";
171
172 assert ( strlen (locale_root)
173 + strlen (src)
174 + strlen (charmap)
175 + 2 < sizeof locale_path);
176
177 strcpy (locale_path, locale_root);
178
179 if (locname) {
180 if (strchr (locname, _RWSTD_PATH_SEP))
181 strcpy (locale_path, locname);
182 else {
183 strcat (locale_path, SLASH);
184 strcat (locale_path, locname);
185 }
186 }
187 else {
188 // compute the locale pathname from `src', `charmap',
189 // and `locale_root'
190 strcpy (locale_path, locale_root);
191 strcat (locale_path, SLASH);
192
193 const char *slash = strrchr (src, _RWSTD_PATH_SEP);
194 slash = slash ? slash + 1 : src;
195
196 strcat (locale_path, src);
197 strcat (locale_path, ".");
198
199 slash = strrchr (charmap, _RWSTD_PATH_SEP);
200 slash = slash ? slash + 1 : charmap;
201
202 strcat (locale_path, slash);
203 }
204
205 // check to see if the locale database already exists and
206 // if so, return immediately the locale filename to the caller
207#if !defined (_MSC_VER)
208 struct stat sb;
209 if (!stat (locale_path, &sb)) {
210#else
211 struct _stat sb;
212 if (!_stat (locale_path, &sb)) {
213#endif
214 return strrchr (locale_path, _RWSTD_PATH_SEP) + 1;
215 }
216
217 // otherwise, try to create the locale database
218
219 // fallback for when TOPDIR is unset or empty
220 char topdir_path_buf [] = __FILE__;
221
222 // use TOPDIR to determine the root of the source tree
223 const char* topdir = getenv (TOPDIR);
224 if (!topdir || !*topdir) {
225
226 // try to get TOPDIR from __FILE__
227 char* const slash = strrchr (topdir_path_buf, _RWSTD_PATH_SEP);
228
229 if (slash) {
230 slash [-1] = '\0';
231 topdir = topdir_path_buf;
232 }
233 }
234
235 if (!topdir || !*topdir) {
236 rw_error (0, __FILE__, __LINE__,
237 "the environment variable %s is %s",
238 TOPDIR, topdir ? "empty" : "undefined");
239 return 0;
240 }
241
242 // use BINDIR to determine the location of the localedef command
243 const char* bindir = getenv (BINDIR);
244 if (!bindir)
245 bindir = ".." SLASH "bin";
246
247 // if `src' is relative pathname (or a filename) construct the fully
248 // qualified absolute pathname to the locale definition file from it
249 char src_path [PATH_MAX];
250
251 if (!IS_ABSOLUTE_PATHNAME (src)) {
252 strcpy (src_path, topdir);
253 strcat (src_path, SLASH RELPATH SLASH "src" SLASH);
254 strcat (src_path, src);
255
256 // if the file doesn't exist, see if there is a file
257 // with that name in the locale root directory (e.g.,
258 // a temporary file)
259 FILE* const file_exists = fopen (src_path, "r");
260 if (file_exists)
261 fclose (file_exists);
262 else {
263 strcpy (src_path, locale_root);
264 strcat (src_path, SLASH);
265 strcat (src_path, src);
266 }
267
268 src = src_path;
269 }
270
271 char charmap_path [PATH_MAX];
272 if (!IS_ABSOLUTE_PATHNAME (charmap)) {
273 strcpy (charmap_path, topdir);
274 strcat (charmap_path, SLASH RELPATH SLASH "charmaps" SLASH);
275 strcat (charmap_path, charmap);
276
277 // if the file doesn't exist, see if there is a file
278 // with that name in the locale root directory (e.g.,
279 // a temporary file)
280 FILE* const file_exists = fopen (charmap_path, "r");
281 if (file_exists)
282 fclose (file_exists);
283 else {
284 strcpy (charmap_path, locale_root);
285 strcat (charmap_path, SLASH);
286 strcat (charmap_path, charmap);
287 }
288
289 charmap = charmap_path;
290 }
291
292 if (!args)
293 args = "";
294
295 const int ret = rw_system ("%s%slocaledef%s %s -c -f %s -i %s %s",
296 bindir, SLASH, EXE_SUFFIX, args,
297 charmap, src, locale_path);
298
299 // return the unqualified locale file name on success or 0 on failure
300 return ret ? (char*)0 : strrchr (locale_path, _RWSTD_PATH_SEP) + 1;
301}
302
303
305extern "C" {
306
307static char rw_locale_root [PATH_MAX];
308
309static void atexit_rm_locale_root ()
310{
311 const bool e = rw_enable (rw_error, false);
312 const bool n = rw_enable (rw_note , false);
313
314 // remove temporary locale databases created by the test
315 rw_system (SHELL_RM_RF "%s", rw_locale_root);
316
317 rw_enable (rw_note , n);
319}
320
321}
322
323_TEST_EXPORT const char*
325{
326 // set any additional environment variables defined in
327 // the RW_PUTENV environment variable (if it exists)
328 rw_putenv (0);
329
330 // create a temporary directory for files created by the test
331 const char* const locale_root = rw_tmpnam (rw_locale_root);
332 if (!locale_root)
333 return 0;
334
335 char envvar [sizeof LOCALE_ROOT_ENVAR + sizeof rw_locale_root] =
337
338 strcat (envvar, locale_root);
339
340 // remove temporary file if mkstemp() rw_tmpnam() called mkstemp()
341 if (rw_system (SHELL_RM_RF " %s", locale_root)) {
342
343#ifdef _WIN32
344 // ignore errors on WIN32 where the stupid DEL command
345 // fails even with /Q /S when the files don't exist
346#else
347 // assume a sane implementation of SHELL_RM_RF
348 return 0;
349#endif // _WIN32
350 }
351
352 if (rw_system ("mkdir %s", locale_root))
353 return 0;
354
355 // set the "RWSTD_LOCALE_ROOT" environment variable
356 // where std::locale looks for locale database files
357 rw_putenv (envvar);
358
359 rw_error (0 == atexit (atexit_rm_locale_root), __FILE__, __LINE__,
360 "atexit(atexit_rm_locale_root) failed: %m");
361
362 return locale_root;
363}
364
365
367_TEST_EXPORT char*
368rw_locales (int loc_cat, const char* grep_exp, bool prepend_c_loc)
369{
370 static char deflocname [3] = "C\0";
371 static char* slocname = 0;
372
373 static const size_t grow_size = 5120;
374 static size_t size = 0; // the number of elements in the array
375 static size_t total_size = grow_size; // the size of the array
376 static int last_cat = loc_cat; // last category
377
378#ifndef _MSC_VER
379# define _QUIET_MALLOC(n) malloc(n)
380# define _QUIET_FREE(p) free(p)
381#else
382 // prevent allocation from causing failures in tests that
383 // keep track of storage allocated in _NORMAL_BLOCKS
384# define _QUIET_MALLOC(n) _malloc_dbg (n, _CLIENT_BLOCK, 0, 0)
385# define _QUIET_FREE(p) _free_dbg (p, _CLIENT_BLOCK);
386#endif
387
388 // allocate first time through
389 if (!slocname) {
390 slocname = _RWSTD_STATIC_CAST (char*, _QUIET_MALLOC (total_size));
391
392 if (!slocname)
393 return deflocname;
394
395 *slocname = '\0';
396 }
397
398 // return immediately if buffer is already initialized
399 if (*slocname && loc_cat == last_cat)
400 return slocname;
401
402 // remmeber the category we were last called with
403 last_cat = loc_cat;
404
405 char* locname = slocname;
406
407 char* save_localename = 0;
408 char namebuf [PATH_MAX];
409
410 if (loc_cat != _UNUSED_CAT) {
411 // copy the locale name, the original may be overwitten by libc
412 save_localename = strcpy (namebuf, setlocale (loc_cat, 0));
413 }
414
415 const char* const fname = rw_tmpnam (0);
416
417 if (!fname) {
418 return deflocname; // error
419 }
420
421 // make sure that grep_exp is <= 80
422 if (grep_exp && 80 < strlen (grep_exp)) {
423 abort ();
424 }
425
426 // execute a shell command and redirect its output into the file
427 const int exit_status =
428 grep_exp && *grep_exp
429 ? rw_system ("locale -a | grep \"%s\" > %s", grep_exp, fname)
430 : rw_system ("locale -a > %s", fname);
431
432 if (exit_status) {
433 return deflocname; // error
434 }
435
436 // open file containing the list of installed locales
437 FILE *file = fopen (fname, "r");
438
439 if (file) {
440
441 char linebuf [256];
442
443 // even simple locale names can be very long (e.g., on HP-UX,
444 // where a locale name always consists of the names of all
445 // categories, such as "C C C C C C")
446 char last_name [256];
447 *last_name = '\0';
448
449 // put the C locale at the front
450 if (prepend_c_loc) {
451 strcpy (locname, deflocname);
452
453 const size_t defnamelen = strlen (deflocname) + 1;
454 locname += defnamelen;
455 size += defnamelen;
456 }
457
458 // if successful, construct a char array with the locales
459 while (fgets (linebuf, int (sizeof linebuf), file)) {
460
461 const size_t linelen = strlen (linebuf);
462
463 linebuf [linelen ? linelen - 1 : 0] = '\0';
464
465 // don't allow C locale to be in the list again
466 // if we put it at the front of the locale list
467 if (prepend_c_loc && !strcmp (linebuf, deflocname))
468 continue;
469
470#ifdef _RWSTD_OS_SUNOS
471
472 const char iso_8859_pfx[] = "iso_8859_";
473
474 // avoid locales named common and iso_8859_* on SunOS
475 // since they are known to cause setlocale() to fail
476 if ( !strcmp ("common", linebuf)
477 || sizeof iso_8859_pfx <= linelen
478 && !memcmp (iso_8859_pfx, linebuf, sizeof iso_8859_pfx - 1))
479 continue;
480
481#endif // _RWSTD_OS_SUNOS
482
483 // if our buffer is full then dynamically allocate a new one
484 size += linelen;
485 if (total_size < size) {
486 total_size += grow_size;
487
488 char* tmp =
489 _RWSTD_STATIC_CAST (char*, _QUIET_MALLOC (total_size));
490 if (!tmp)
491 break;
492
493 memcpy (tmp, slocname, total_size - grow_size);
494
495 _QUIET_FREE (slocname);
496
497 slocname = tmp;
498 locname = slocname + size - linelen;
499 }
500
501#ifdef _WIN64
502
503 // prevent a hang (OS/libc bug?)
504 strcpy (locname, linebuf);
505 locname += linelen;
506
507#else // if !defined (_WIN64)
508 if (loc_cat != _UNUSED_CAT) {
509
510 // set the C locale to verify that the name is valid
511 const char *name = setlocale (loc_cat, linebuf);
512
513 // if it is and if the actual locale name different
514 // from the last one, append it to the list
515 if (name && strcmp (last_name, name)) {
516 strcpy (locname, linebuf);
517 locname += linelen;
518
519 // save the last locale name
520 assert (strlen (name) < sizeof last_name);
521 strcpy (last_name, name);
522 }
523 }
524 else {
525 strcpy (locname, linebuf);
526 locname += linelen;
527 }
528
529#endif // _WIN64
530
531 }
532 *locname = '\0';
533 }
534
535 if (loc_cat != _UNUSED_CAT)
536 setlocale (loc_cat, save_localename);
537
538 // close before removing
539 fclose (file);
540
541 remove (fname);
542
543 return *slocname ? slocname : deflocname;
544}
545
546
548// finds a multibyte character that is `bytes' long if `bytes' is less
549// than or equal to MB_CUR_MAX, or the longest multibyte sequence in
550// the current locale
551static const char*
552_get_mb_char (char *buf, size_t bytes)
553{
554 _RWSTD_ASSERT (0 != buf);
555
556 *buf = '\0';
557
558 if (0 == bytes)
559 return buf;
560
561 const bool exact = bytes <= size_t (MB_CUR_MAX);
562
563 if (!exact)
565
566 wchar_t wc;
567
568 // search the first 64K characters sequentially
569 for (wc = wchar_t (1); wc != wchar_t (0xffff); ++wc) {
570
571 if ( int (bytes) == wctomb (buf, wc)
572 && int (bytes) == mblen (buf, bytes)) {
573 // NUL-terminate the multibyte character of the requested length
574 buf [bytes] = '\0';
575 break;
576 }
577
578 *buf = '\0';
579 }
580
581#if 2 < _RWSTD_WCHAR_SIZE
582
583 // if a multibyte character of the requested size is not found
584 // in the low 64K range, try to find one using a random search
585 if (wchar_t (0xffff) == wc) {
586
587 // iterate only so many times to prevent an infinite loop
588 // in case when MB_CUR_MAX is greater than the longest
589 // multibyte character
590 for (int i = 0; i != 0x100000; ++i) {
591
592 wc = wchar_t (rand ());
593
594 if (RAND_MAX < 0x10000) {
595 wc <<= 16;
596 wc |= wchar_t (rand ());
597 }
598
599 if ( int (bytes) == wctomb (buf, wc)
600 && int (bytes) == mblen (buf, bytes)) {
601 // NUL-terminate the multibyte character
602 buf [bytes] = '\0';
603 break;
604 }
605
606 *buf = '\0';
607 }
608 }
609
610#endif // 2 < _RWSTD_WCHAR_SIZE
611
612 // return 0 on failure to find a sequence exactly `bytes' long
613 return !exact || bytes == strlen (buf) ? buf : 0;
614}
615
616
617_TEST_EXPORT size_t
619{
620 _RWSTD_ASSERT (0 != mb_chars);
621
622 const char* mbc = _get_mb_char (mb_chars [0], size_t (-1));
623
624 if (0 == rw_note (0 != mbc, __FILE__, __LINE__,
625 "failed to find any multibyte characters "
626 "in locale \"%s\" with MB_CUR_MAX = %u",
627 setlocale (LC_CTYPE, 0), MB_CUR_MAX))
628 return 0;
629
630 size_t mb_cur_max = strlen (mbc);
631
632 if (_RWSTD_MB_LEN_MAX < mb_cur_max)
633 mb_cur_max = _RWSTD_MB_LEN_MAX;
634
635 // fill each element of `mb_chars' with a multibyte character
636 // of the corresponding length
637 for (size_t i = mb_cur_max; i; --i) {
638
639 // try to generate a multibyte character `i' bytes long
640 mbc = _get_mb_char (mb_chars [i - 1], i);
641
642 if (0 == mbc) {
643 // zh_CN.gb18030 and zh_TW.euctw on Linux are examples
644 // of multibyte locales where MB_CUR_MAX == 4 but,
645 // apparently, no 3-byte characters
646 if (0 == rw_note (mb_cur_max <= i, __FILE__, __LINE__,
647 "failed to find %u-byte characters "
648 "in locale \"%s\" with MB_CUR_MAX = %u",
649 i, setlocale (LC_CTYPE, 0), MB_CUR_MAX)) {
650 mb_cur_max = 0;
651 break;
652 }
653 --mb_cur_max;
654 }
655 }
656
657 return mb_cur_max;
658}
659
660
661_TEST_EXPORT size_t
662rw_get_wchars (wchar_t *wbuf, size_t bufsize, int nbytes /* = 0 */)
663{
664 if (0 == bufsize)
665 return 0;
666
667 char tmp [_RWSTD_MB_LEN_MAX];
668
669 size_t nchars = 0;
670
671 for (int i = 0; i != 65536; ++i) {
672
673 // determine whether the wide character is valid
674 // and if so, the length of the multibyte character
675 // that corresponds to it
676 const wchar_t wc = wchar_t (i);
677 const int len = wctomb (tmp, wc);
678
679 if ((nbytes == 0 && 0 < len) || (nbytes != 0 && nbytes == len)) {
680 // if the requested length is 0 (i.e., the caller doesn't
681 // care) and the character is valid, store it
682 // if the requested length is non-zero (including -1),
683 // and the value returned from mblen() is the same, store
684 // it (this makes it possible to find invalid characters
685 // as well as valid ones)
686 wbuf [nchars++] = wc;
687 if (nchars == bufsize)
688 return nchars;
689 }
690 }
691
692#if 2 < _RWSTD_WCHAR_SIZE
693
694 // try to find the remaining wide characters by a random
695 // search, iterating only so many times to prevent an
696 // infinite loop
697 for (int i = 0; i != 0x100000; ++i) {
698
699 // make a wide character with a random bit pattern
700 wchar_t wc = wchar_t (rand ());
701
702 if (RAND_MAX < 0x10000) {
703 wc <<= 16;
704 wc |= wchar_t (rand ());
705 }
706
707 const int len = wctomb (tmp, wc);
708
709 if ((nbytes == 0 && 0 < len) || (nbytes != 0 && nbytes == len)) {
710 wbuf [nchars++] = wc;
711 if (nchars == bufsize)
712 return nchars;
713 }
714 }
715
716#endif // 2 < _RWSTD_WCHAR_SIZE
717
718 return nchars;
719
720}
721
722
723_TEST_EXPORT const char*
724rw_find_mb_locale (size_t *mb_cur_max,
725 rw_mbchar_array_t mb_chars)
726{
727 _RWSTD_ASSERT (0 != mb_cur_max);
728 _RWSTD_ASSERT (0 != mb_chars);
729
730 if (2 > _RWSTD_MB_LEN_MAX) {
731 rw_warn (0, __FILE__, __LINE__, "MB_LEN_MAX = %d, giving up",
732 _RWSTD_MB_LEN_MAX);
733 return 0;
734 }
735
736 static const char *mb_locale_name;
737
738 char saved_locale_name [1024];
739 strcpy (saved_locale_name, setlocale (LC_CTYPE, 0));
740
741 _RWSTD_ASSERT (strlen (saved_locale_name) < sizeof saved_locale_name);
742
743 *mb_cur_max = 0;
744
745 // iterate over all installed locales
746 for (const char *name = rw_locales (_RWSTD_LC_CTYPE, 0); name && *name;
747 name += strlen (name) + 1) {
748
749 if (setlocale (LC_CTYPE, name)) {
750
751 // try to generate a set of multibyte characters
752 // with lengths from 1 and MB_CUR_MAX (or less)
753 const size_t cur_max = rw_get_mb_chars (mb_chars);
754
755 if (*mb_cur_max < cur_max) {
756 *mb_cur_max = cur_max;
757 mb_locale_name = name;
758
759 // break when we've found a multibyte locale
760 // with the longest possible encoding
761 if (_RWSTD_MB_LEN_MAX == *mb_cur_max)
762 break;
763 }
764 }
765 }
766
767 if (*mb_cur_max < 2) {
768 rw_warn (0, __FILE__, __LINE__,
769 "failed to find a full set of multibyte "
770 "characters in locale \"%s\" with MB_CUR_MAX = %u "
771 "(computed)", mb_locale_name, *mb_cur_max);
772 mb_locale_name = 0;
773 }
774 else {
775 // (re)generate the multibyte characters for the saved locale
776 // as they may have been overwritten in subsequent iterations
777 // of the loop above (while searching for a locale with greater
778 // value of MB_CUR_MAX)
779 setlocale (LC_CTYPE, mb_locale_name);
780 rw_get_mb_chars (mb_chars);
781 }
782
783 setlocale (LC_CTYPE, saved_locale_name);
784
785 return mb_locale_name;
786}
787
788
789
791_TEST_EXPORT const char*
792rw_create_locale (const char *charmap, const char *locale)
793{
794 // only one locale is enough (avoid invoking localedef more than once)
795 static const char* locname;
796 const char* locale_root;
797
798 if (locname)
799 return locname;
800
801 // set up RWSTD_LOCALE_ROOT and other environment variables
803
804 if (0 == locale_root)
805 return 0;
806
807 // create a temporary locale definition file that exercises as
808 // many different parts of the collate standard as possible
809 char srcfname [PATH_MAX];
810 if (rw_snprintf (srcfname, PATH_MAX, "%s%slocale.src",
811 locale_root, SLASH) < 0) {
812 return 0;
813 }
814
815 FILE *fout = fopen (srcfname, "w");
816
817 if (!fout) {
818 rw_error (0, __FILE__, __LINE__,
819 "fopen(#%s, \"w\") failed: %m", srcfname);
820 return 0;
821 }
822
823 fprintf (fout, "%s", locale);
824
825 fclose (fout);
826
827 // create a temporary character map file
828 char cmfname [PATH_MAX];
829 if (rw_snprintf (cmfname, PATH_MAX, "%s%scharmap.src",
830 locale_root, SLASH) < 0) {
831 return 0;
832 }
833
834 fout = fopen (cmfname, "w");
835
836 if (!fout) {
837 rw_error (0, __FILE__, __LINE__,
838 "fopen(%#s, \"w\") failed: %m", cmfname);
839 return 0;
840 }
841
842 fprintf (fout, "%s", charmap);
843
844 fclose (fout);
845
846 locname = "test-locale";
847
848 // process the locale definition file and character map
849 if (0 == rw_localedef ("-w", srcfname, cmfname, locname))
850 locname = 0;
851
852 return locname;
853}
854
855
856
858static const char*
859_rw_locale_names;
860
861_TEST_EXPORT const char* const&
862rw_opt_locales = _rw_locale_names;
863
864
865_TEST_EXPORT int
866rw_opt_setlocales (int argc, char* argv[])
867{
868 if (1 == argc && argv && 0 == argv [0]) {
869 static const char helpstr[] = {
870 "Use the locales specified by the space-parated list of locale"
871 "names given by <arg>.\n"
872 };
873
874 argv [0] = _RWSTD_CONST_CAST (char*, helpstr);
875
876 return 0;
877 }
878
879 // the option requires an equals sign followed by an optional argument
880 char *args = strchr (argv [0], '=');
881
882 RW_ASSERT (0 != args);
883
884 // small static buffer should be sufficient in most cases
885 static char buffer [256];
886
887 const size_t len = strlen (++args);
888
889 // dynamically allocate a bigger buffer when the small buffer
890 // isn't big enough (let the dynamically allocated buffer leak)
891 char* const locale_names =
892 sizeof buffer < len + 2 ? (char*)malloc (len + 2) : buffer;
893
894 if (0 == locale_names)
895 return 1;
896
897 locale_names [len] = '\0';
898 locale_names [len + 1] = '\0';
899
900 memcpy (locale_names, args, len);
901
902 for (char *next = locale_names; ; ) {
903 next = strpbrk (next, ", ");
904 if (next)
905 *next++ = '\0';
906 else
907 break;
908 }
909
910 _rw_locale_names = locale_names;
911
912 // return 0 on success
913 return 0;
914}
915
916
917
919_TEST_EXPORT int
920rw_create_catalog (const char * catname, const char * catalog)
921{
922 RW_ASSERT (catname && catalog);
923
924 FILE* const f = fopen (catname, "w");
925
926 if (!f)
927 return -1;
928
929#ifndef _WIN32
930
931 for (int i = 1; *catalog; ++catalog, ++i) {
932 fprintf (f, "$set %d This is Set %d\n", i, i);
933 for (int j = 1; *catalog; catalog += strlen (catalog) + 1, ++j)
934 fprintf (f, "%d %s\n", j, catalog);
935 }
936
937#else // if defined (_WIN32)
938
939 fprintf (f, "STRINGTABLE\nBEGIN\n");
940
941 for (int i = 1; *catalog; ++catalog) {
942 for (; *catalog; catalog += strlen (catalog) + 1, ++i)
943 fprintf (f, "%d \"%s\"\n", i, catalog);
944 }
945
946 fprintf (f, "END\n");
947
948#endif // _WIN32
949
950 fclose (f);
951
952 char *cat_name = new char [strlen (catname) + 1];
953 strcpy (cat_name, catname);
954 if (char *dot = strrchr (cat_name, '.'))
955 *dot = '\0';
956
957 const int ret = rw_system ("gencat %s" RW_CAT_EXT " %s",
958 cat_name, catname);
959
960 delete[] cat_name;
961
962 remove (catname);
963
964 return ret;
965}
966
967inline bool
968_rw_isspace (char ch)
969{
970 return 0 != isspace ((unsigned char)ch);
971}
972
973inline char
974_rw_toupper (char ch)
975{
976 return toupper ((unsigned char)ch);
977}
978
979inline char
980_rw_tolower (char ch)
981{
982 return tolower ((unsigned char)ch);
983}
984
985// our locale database is a big array of these
987 char locale_name [64]; // English_United States.1252
988 char canonical_name [32]; // en-US-1-1252
990};
991
994 _RWSTD_SIZE_T count;
995};
996
998 const char* native;
999 const char* canonical;
1000};
1001
1002extern "C" {
1003
1004static int
1005_rw_lookup_comparator (const void* _lhs, const void* _rhs)
1006{
1007 const _rw_lookup_entry_t* lhs = (const _rw_lookup_entry_t*)_lhs;
1008 const _rw_lookup_entry_t* rhs = (const _rw_lookup_entry_t*)_rhs;
1009
1010 return strcmp (lhs->native, rhs->native);
1011}
1012
1013} // extern "C"
1014
1016
1018 : entries_ (0), count_ (0), table_data_ (0) {
1019 }
1020
1022 if (entries_)
1023 free (entries_);
1024 entries_ = 0;
1025 count_ = 0;
1026
1027 if (table_data_)
1028 free (table_data_);
1029 table_data_ = 0;
1030 }
1031
1032 bool load_from_file (const char* path, const char* file, int upper_or_lower);
1033
1035 return entries_;
1036 }
1037
1038 size_t get_num_entries () const {
1039 return count_;
1040 }
1041
1042 const char* get_canonical_name (const char* name) const;
1043
1044private:
1045
1046 _rw_lookup_entry_t* entries_;
1047 size_t count_;
1048 char* table_data_;
1049
1050private:
1051 // intentionally hidden
1053 _rw_lookup_table_t& operator= (const _rw_lookup_table_t& rhs);
1054};
1055
1056
1057static void
1058_rw_reset_locales (_rw_locale_array* a)
1059{
1060 // reset the next pointers so that all locales are included
1061 for (size_t i = 0; i < a->count; ++i)
1062 a->entries [i].next = &a->entries [i+1];
1063 a->entries [a->count - 1].next = 0;
1064}
1065
1066//
1067// this function gets a list of all of the locales that are installed. it
1068// only queries the system once and caches the result for use in future
1069// requests.
1070//
1071static _rw_locale_array
1072_rw_all_locales ()
1073{
1074 static _rw_locale_array result;
1075
1076 // if we have already collection, reuse it
1077 if (result.entries && result.count != 0) {
1078 _rw_reset_locales (&result);
1079 return result;
1080 }
1081
1082 static _rw_locale_entry fallback = {
1083 "C", "C", 0
1084 };
1085
1086 result.entries = &fallback;
1087 result.count = 1;
1088
1089 const char* const fname = rw_tmpnam (0);
1090 if (!fname) {
1091 return result;
1092 }
1093
1094 const int status = rw_system ("locale -a > %s", fname);
1095 if (status) {
1096 return result;
1097 }
1098
1099 FILE* file = fopen (fname, "r");
1100 if (file) {
1101
1102 // looks to be the first time, get a list of all locales
1103 const size_t entry_size = sizeof (_rw_locale_entry);
1104 const size_t grow_size = 64;
1105
1106 _rw_locale_entry* entries = 0;
1107 size_t capacity = 0;
1108 size_t size = 0;
1109
1110 // load the native to canonical lookup table
1111 _rw_lookup_table_t languages_map;
1112 _rw_lookup_table_t countries_map;
1113 _rw_lookup_table_t encodings_map;
1114
1115 // fallback for when TOPDIR is unset or empty
1116 char topdir_path_buf [] = __FILE__;
1117
1118 // use TOPDIR to determine the root of the source tree
1119 const char* topdir = getenv (TOPDIR);
1120 if (!topdir || !*topdir) {
1121
1122 // try to get TOPDIR from __FILE__
1123 char* const slash = strrchr (topdir_path_buf, _RWSTD_PATH_SEP);
1124
1125 if (slash) {
1126 slash [-1] = '\0';
1127 topdir = topdir_path_buf;
1128 }
1129 }
1130
1131 if (!topdir || !*topdir) {
1132 rw_error (0, __FILE__, __LINE__,
1133 "the environment variable %s is %s",
1134 TOPDIR, topdir ? "empty" : "undefined");
1135 }
1136 else {
1137 // we should be loading this from some other well
1138 // known path so we don't depend on $TOPDIR. sadly
1139 // __FILE__ is not an absolute path on msvc
1140
1141 char path [PATH_MAX];
1142 strcpy (path, topdir);
1143 strcat (path, SLASH RELPATH SLASH);
1144
1145 // load mapping from local to canonical names
1146 languages_map.load_from_file (path, "languages", -1);
1147 countries_map.load_from_file (path, "countries", 1);
1148 encodings_map.load_from_file (path, "encodings", 1);
1149 }
1150
1151 char locale [128];
1152 while (fgets (locale, int (sizeof (locale)), file)) {
1153
1154 // ensure sufficient space in array
1155 if (! (size < capacity)) {
1156 capacity += grow_size;
1157
1158 _rw_locale_entry* new_entries =
1160 _QUIET_MALLOC (entry_size * capacity));
1161 if (!new_entries) {
1162 break;
1163 }
1164
1165 memcpy (new_entries, entries, entry_size * size);
1166
1167 // deallocate the old buffer
1168 _QUIET_FREE (entries);
1169
1170 entries = new_entries;
1171 }
1172
1173 // grab entry to update
1174 _rw_locale_entry* const entry = &entries [size];
1175 entry->next = 0;
1176
1177 const size_t len = strlen (locale);
1178 locale [len ? len - 1 : 0] = '\0';
1179
1180 // make sure that the named locale is one that we can use
1181 if (!setlocale (LC_CTYPE, locale)) {
1182
1183 rw_note (0, __FILE__, __LINE__,
1184 "setlocale() failed for '%s'", locale);
1185
1186 continue;
1187
1188 }
1189
1190 // is not an alias for the C or POSIX locale
1191 else if (!strcmp (locale, "C") || !strcmp (locale, "POSIX")) {
1192 continue; // we don't do C/POSIX locale
1193 }
1194
1195 // has a name that is short enough for our buffer
1196 else if (sizeof (entry->locale_name) < len) {
1197
1198 rw_note (0, __FILE__, __LINE__,
1199 "locale name '%s' was to long for fixed buffer",
1200 locale);
1201
1202 continue; // locale name didn't fit, so we skip it
1203 }
1204
1205#ifndef _RWSTD_NO_LANGINFO
1206 char codeset [40];
1207
1208 int i = 0;
1209 for (const char* charset = nl_langinfo (CODESET);
1210 *charset;
1211 ++charset) {
1212 codeset [i++] = _rw_toupper (*charset);
1213 }
1214
1215 codeset [i] = '\0';
1216#endif
1217
1218 // copy the locale name
1219 strcpy (entry->locale_name, locale);
1220
1221 // attempt to split line into parts
1222 char* extension = strrchr (locale, '@');
1223 if (extension) {
1224 *extension++ = '\0';
1225 }
1226
1227 char* encoding = strrchr (locale, '.');
1228 if (encoding) {
1229 *encoding++ = '\0';
1230
1231 for (int n = 0; encoding [n]; ++n)
1232 encoding [n] = _rw_toupper (encoding [n]);
1233 }
1234
1235 char* country = strrchr (locale, '_');
1236 if (country) {
1237 *country++ = '\0';
1238
1239 for (int n = 0; country [n]; ++n)
1240 country [n] = _rw_toupper (country [n]);
1241 }
1242
1243 char* language = locale;
1244
1245 for (int n = 0; language [n]; ++n)
1246 language [n] = _rw_tolower (language [n]);
1247
1248 // use mapping databases to find the canonical
1249 // names for each part of the locale name
1250
1251 const char* planguage =
1252 languages_map.get_canonical_name (language);
1253 if (!planguage)
1254 planguage = language;
1255
1256 // if country name was provided, then lookup in the country
1257 // mapping. otherwise use language to guess country.
1258 const char* pcountry =
1259 countries_map.get_canonical_name (country);
1260 if (!pcountry)
1261 pcountry = country;
1262
1263#ifndef _RWSTD_NO_LANGINFO
1264 const char* pencoding =
1265 encodings_map.get_canonical_name (codeset);
1266 if (!pencoding)
1267 pencoding = codeset;
1268#else
1269 const char* pencoding =
1270 encodings_map.get_canonical_name (encoding);
1271 if (!pencoding)
1272 pencoding = encoding;
1273#endif
1274
1275 // require all three mappings are valid
1276 if (!planguage || !*planguage) {
1277
1278 //rw_note (0, __FILE__, __LINE__,
1279 // "failed to get language for locale '%s'",
1280 // entry->locale_name);
1281
1282 continue;
1283 }
1284 else if (!pcountry || !*pcountry) {
1285
1286 //rw_note (0, __FILE__, __LINE__,
1287 // "failed to get country for locale '%s'",
1288 // entry->locale_name);
1289
1290 continue;
1291 }
1292 else if (!pencoding || !*pencoding) {
1293
1294 //rw_note (0, __FILE__, __LINE__,
1295 // "failed to get codeset for locale '%s'",
1296 // entry->locale_name);
1297
1298 continue;
1299 }
1300
1301 // the canonical name for lookup
1302 sprintf (entry->canonical_name, "%s-%s-%d-%s",
1303 planguage, pcountry, int (MB_CUR_MAX), pencoding);
1304
1305 //
1306 // eliminate locales that are duplicates according to
1307 // canonical name. we do this because the setlocale()
1308 // doesn't seem to tell us about aliases.
1309 //
1310
1311 bool duplicate = false;
1312
1313 // search backward as matches are more likely to be near
1314 // the back
1315 for (size_t e = size; 0 != e; --e) {
1316
1317 if (!strcmp (entries [e-1].canonical_name,
1318 entry->canonical_name)) {
1319
1320 //rw_note (0, __FILE__, __LINE__,
1321 // "ignoring duplicate locale '%s'",
1322 // entry->locale_name);
1323
1324 duplicate = true;
1325
1326 break;
1327 }
1328 }
1329
1330 if (!duplicate)
1331 size += 1;
1332 }
1333
1334 fclose (file);
1335
1336 // delete temp file
1337 remove (fname);
1338
1339 // link all of the nodes into result
1340 if (size) {
1341 result.entries = entries;
1342 result.count = size;
1343 }
1344 else
1345 _QUIET_FREE (entries);
1346 }
1347
1348 // link each node to the next. if the array is sorted,
1349 // the list will be sorted.
1350 _rw_reset_locales (&result);
1351
1352 return result;
1353}
1354
1355_TEST_EXPORT char*
1356rw_locale_query (int loc_cat, const char* query, size_t wanted)
1357{
1358 // query format <language>-<COUNTRY>-<MB_CUR_LEN>-<CODESET>
1359
1360 // the null query string will return any locale
1361 if (!query)
1362 query = "*";
1363
1364 if (!wanted)
1365 wanted = _RWSTD_SIZE_MAX;
1366
1367 char buf [256];
1368
1369 // get a brace expanded representation of query, each expansion
1370 // is a null terminated string. the entire buffer is also null
1371 // terminated
1372 char* res = rw_shell_expand (query, 0, buf, sizeof (buf), '\0');
1373 if (!res)
1374 return 0;
1375
1376 // cache the locale name so we can restore later, this must happen
1377 // before _rw_all_locales() because that function just changes the
1378 // locale without restoring it
1379 char save_locale [PATH_MAX];
1380 strcpy (save_locale, setlocale (LC_ALL, 0));
1381
1382 const _rw_locale_array all = _rw_all_locales ();
1383
1384 // make these local and require the user to deallocate
1385 // with free?
1386 static char* string = 0;
1387 static size_t length = 0;
1388 static size_t capacity = 0;
1389
1390 _rw_locale_entry rejects;
1391 rejects.canonical_name [0] = '\0';
1392 rejects.locale_name [0] = '\0';
1393 rejects.next = all.entries;
1394
1395 // for each result locale name
1396 size_t count = 0;
1397 for (const char* name = res; *name; name += strlen (name) + 1)
1398 {
1399 _rw_locale_entry* dummy = &rejects;
1400
1401 // linear search for matches in the reject list
1402 while (dummy->next)
1403 {
1404 // append name to the output buffer
1405 const _rw_locale_entry* entry = dummy->next;
1406
1407 // see if we found a match
1408 if (rw_fnmatch (name, entry->canonical_name, 0)) {
1409
1410 // not a match, advance past it leaving it in the
1411 // rejects list
1412 dummy = dummy->next;
1413
1414 // and move along to next one
1415 continue;
1416
1417 }
1418
1419 // remove the accepted entry from the reject list
1420 // so we will not include it again
1421 dummy->next = entry->next;
1422
1423 // if the user requested locales from a specific category
1424 if (loc_cat != _UNUSED_CAT) {
1425
1426 // make sure that the matching locale has the specified
1427 // locale category and that we can use it.
1428 if (!setlocale (loc_cat, entry->locale_name)) {
1429
1430 // if we can't use it, then bail. this effectively
1431 // removes the locale from the rejects list and
1432 // doesn't add it to the accepted list.
1433 continue;
1434 }
1435 }
1436
1437 const size_t add_length = strlen (entry->locale_name) + 1;
1438 const size_t new_length = length + add_length;
1439
1440 // grow buffer if necessary
1441 if (! (new_length < capacity)) {
1442
1443 while (capacity < new_length)
1444 capacity += 256;
1445
1446 // one additional character for the second null
1447 char* new_string =
1448 _RWSTD_STATIC_CAST(char*, _QUIET_MALLOC (capacity + 1));
1449 if (!new_string) {
1450
1451 // setup to get out of outer loop
1452 count = wanted;
1453
1454 // get out of inner loop
1455 break;
1456 }
1457
1458 memcpy (new_string, string, length);
1459
1460 _QUIET_FREE (string);
1461
1462 string = new_string;
1463 }
1464
1465 // append the name, and update the length
1466 memcpy (string + length, entry->locale_name, add_length);
1467
1468 length = new_length;
1469
1470 count += 1;
1471 if (! (count < wanted))
1472 break;
1473 }
1474
1475 if (! (count < wanted))
1476 break;
1477 }
1478
1479 // restore the previous locale
1480 setlocale (LC_ALL, save_locale);
1481
1482 // deallocate the shell expand buffer if needed
1483 if (res != buf)
1484 free (res);
1485
1486 // double null terminated
1487 if (string) {
1488 string [length+0] = '\0';
1489 string [length+1] = '\0';
1490 }
1491
1492 return string;
1493}
1494
1495
1496const char*
1498{
1499 if (!name)
1500 return 0; // don't search for null string
1501
1502 const _rw_lookup_entry_t entry = { name, 0 };
1503
1504 const _rw_lookup_entry_t* found =
1505 (const _rw_lookup_entry_t*)bsearch (&entry,
1506 entries_,
1507 count_,
1508 sizeof (_rw_lookup_entry_t),
1509 _rw_lookup_comparator);
1510 if (found)
1511 return found->canonical;
1512
1513 return 0;
1514}
1515
1516bool
1517_rw_lookup_table_t::load_from_file (const char* path, const char* name, int upper_or_lower)
1518{
1519 if (entries_)
1520 return false; // should never happen
1521
1522 // buffer overflow!
1523 char filename [PATH_MAX];
1524 strcpy (filename, path);
1525 strcat (filename, name);
1526
1527 FILE* file = fopen (filename, "rb");
1528 if (file) {
1529
1530 // get the size of the file in bytes
1531 fseek (file, 0, SEEK_END);
1532 const size_t table_data_size = ftell (file);
1533 fseek (file, 0, SEEK_SET);
1534
1535 char* table_data =
1536 (char*)malloc (table_data_size + 1);
1537
1538 if (!table_data) {
1539 fclose (file);
1540 return false;
1541 }
1542
1543 // read the entire file into the data buffer
1544 const size_t bytes_read =
1545 fread (table_data, 1, table_data_size, file);
1546 if (bytes_read != table_data_size) {
1547 free (table_data);
1548 fclose (file);
1549 return false;
1550 }
1551
1552 // null terminate
1553 table_data [bytes_read] = '\0';
1554
1555 const size_t entry_size = sizeof (_rw_lookup_entry_t);
1556
1557 _rw_lookup_entry_t* entries = 0;
1558 size_t capacity = 0;
1559 size_t size = 0;
1560
1561 const char* canonical_name = 0;
1562
1563 for (size_t offset = 0; offset < bytes_read; ) {
1564
1565 char* key = table_data + offset;
1566
1567 const size_t len = strcspn (key, "\r\n");
1568 key [len] = '\0';
1569
1570 // skip the newline if it is there
1571 offset += (len + 1);
1572
1573 // special handling for line ends and comments
1574 if (!*key || *key == '\n'
1575 || *key == '#')
1576 continue;
1577
1578 // make upper or lower case as requested
1579 if (upper_or_lower < 0) {
1580 for (char* s = key; *s; ++s)
1581 *s = _rw_tolower (*s);
1582 }
1583 else if (0 < upper_or_lower) {
1584 for (char* s = key; *s; ++s)
1585 *s = _rw_toupper (*s);
1586 }
1587
1588 // if first character of new line is not whitespace, then we have a new
1589 // canonical name token
1590 if (!_rw_isspace (*key)) {
1591
1592 canonical_name = key;
1593
1594 // increment key past cannonical name
1595 for (; *key; ++key)
1596 if (_rw_isspace (*key))
1597 break;
1598 }
1599
1600 // kill whitespace
1601 while (_rw_isspace (*key))
1602 *key++ = '\0';
1603
1604 // key points to first non-whitespace after canonical name
1605
1606 while (*key) {
1607
1608 // key is first non-whitespace character, which is the
1609 // next native name we should record
1610 const char* native_name = key;
1611
1612 // find first comma character, that is the end of the
1613 // native name
1614 while (*key && *key != ',')
1615 ++key;
1616
1617 // if we found a comma, setup next name
1618 if (*key)
1619 *key++ = '\0';
1620
1621 // kill any whitespace before comma
1622 for (char* bey = key - 1; _rw_isspace (*bey); --bey)
1623 *bey = '\0';
1624
1625 // kill whitespace after comma
1626 while (_rw_isspace (*key))
1627 *key++ = '\0';
1628
1629 // ensure we have enough entries
1630 if (! (size < capacity)) {
1631
1632 capacity += 64;
1633
1634 _rw_lookup_entry_t* new_entries =
1635 (_rw_lookup_entry_t*)malloc (entry_size * capacity);
1636 if (!new_entries) {
1637
1638 free (entries);
1639
1640 free (table_data);
1641
1642 fclose (file);
1643
1644 return false;
1645 }
1646
1647 memcpy (new_entries, entries, entry_size * size);
1648
1649 free (entries);
1650
1651 entries = new_entries;
1652 }
1653
1654 // add the new mapping entry
1655 _rw_lookup_entry_t* const entry = &entries [size];
1656 entry->native = native_name;
1657 entry->canonical = canonical_name;
1658
1659 // increment number of entries
1660 size += 1;
1661 }
1662 }
1663
1664 fclose (file);
1665
1666 // sort the entries by native name for efficient searching
1667 qsort (entries, size, entry_size, _rw_lookup_comparator);
1668
1669 // setup the table for return
1670 entries_ = entries;
1671 count_ = size;
1672 table_data_ = table_data;
1673 }
1674 else {
1675 rw_error (0, __FILE__, __LINE__,
1676 "failed to open the file %s", filename);
1677 }
1678
1679 return true;
1680}
int exit_status
void count(T, T, const U &,...)
#define i
Definition 17.names.cpp:126
#define rhs
Definition 17.names.cpp:536
#define e
Definition 17.names.cpp:118
#define nchars
Definition 17.names.cpp:502
#define found
Definition 17.names.cpp:470
#define res
Definition 17.names.cpp:531
#define sb
Definition 17.names.cpp:542
#define f
Definition 17.names.cpp:119
#define a
Definition 17.names.cpp:109
#define lhs
Definition 17.names.cpp:489
#define buf
Definition 17.names.cpp:452
#define j
Definition 17.names.cpp:127
#define buffer
Definition 17.names.cpp:453
#define offset
Definition 17.names.cpp:508
#define tmp
Definition 17.names.cpp:553
#define s
#define len
Definition 17.names.cpp:488
#define n
Definition 17.names.cpp:130
volatile int result
const char * locale_root
IMPORT_C SInt32 atexit(Void(*function)(Void))
#define assert(expr)
Definition _cassert.h:40
#define LC_CTYPE
#define LC_ALL
unsigned short wchar_t
#define _RWSTD_PATH_SEP
Definition _config.h:297
#define SEEK_SET
#define FILE
#define SEEK_END
#define MB_CUR_MAX
#define RAND_MAX
#define _RWSTD_STATIC_CAST(T, x)
Definition _defs.h:534
#define _RWSTD_ASSERT(ignore)
Definition _defs.h:1108
#define _RWSTD_CONST_CAST(T, x)
Definition _defs.h:536
A TOML path.
Definition path.hpp:209
const char * name
Definition codecvt1.cpp:72
UChar bytes[sizeof(int)]
Definition printf.cpp:83
_TEST_EXPORT char * rw_shell_expand(const char *shell_expr, _RWSTD_SIZE_T sz, char *s, _RWSTD_SIZE_T n, char sep)
_TEST_EXPORT bool rw_enable(int(*fun)(int, const char *, int, const char *,...), bool enable=true)
Definition driver.cpp:1698
_TEST_EXPORT int rw_note(int, const char *, int, const char *,...)
Definition driver.cpp:1664
_TEST_EXPORT int rw_error(int, const char *, int, const char *,...)
Definition driver.cpp:1613
_TEST_EXPORT int rw_warn(int, const char *, int, const char *,...)
Definition driver.cpp:1647
_TEST_EXPORT int rw_putenv(const char *, int=-1)
Definition environ.cpp:69
_TEST_EXPORT const char * rw_tmpnam(char *)
Definition file.cpp:206
#define SHELL_RM_RF
Definition rw_file.h:58
_TEST_EXPORT int rw_fnmatch(const char *, const char *, int)
Definition fnmatch.cpp:127
#define LOCALE_ROOT_ENVAR
Definition rw_locale.h:57
char rw_mbchar_array_t[_RWSTD_MB_LEN_MAX][_RWSTD_MB_LEN_MAX]
Definition rw_locale.h:95
#define _UNUSED_CAT
Definition rw_locale.h:36
_TEST_EXPORT int rw_snprintf(char *, _RWSTD_SIZE_T, const char *,...)
_TEST_EXPORT int rw_system(const char *,...)
Definition process.cpp:328
#define _TEST_EXPORT
Definition rw_testdefs.h:55
#define RW_ASSERT(expr)
_RWSTD_SIZE_T count
Definition locale.cpp:994
_rw_locale_entry * entries
Definition locale.cpp:993
Definition locale.cpp:986
struct _rw_locale_entry * next
Definition locale.cpp:989
char locale_name[64]
Definition locale.cpp:987
char canonical_name[32]
Definition locale.cpp:988
Definition locale.cpp:997
const char * native
Definition locale.cpp:998
const char * canonical
Definition locale.cpp:999
const char * get_canonical_name(const char *name) const
Definition locale.cpp:1497
size_t get_num_entries() const
Definition locale.cpp:1038
bool load_from_file(const char *path, const char *file, int upper_or_lower)
Definition locale.cpp:1517
const _rw_lookup_entry_t * get_entries() const
Definition locale.cpp:1034
_TEST_EXPORT char * rw_locale_query(int loc_cat, const char *query, size_t wanted)
Definition locale.cpp:1356
#define RW_CAT_EXT
Definition locale.cpp:124
#define _QUIET_MALLOC(n)
#define BINDIR
Definition locale.cpp:103
char _rw_tolower(char ch)
Definition locale.cpp:980
_TEST_EXPORT const char * rw_localedef(const char *args, const char *src, const char *charmap, const char *locname)
Definition locale.cpp:154
_TEST_EXPORT const char * rw_create_locale(const char *charmap, const char *locale)
Definition locale.cpp:792
_TEST_EXPORT size_t rw_get_wchars(wchar_t *wbuf, size_t bufsize, int nbytes)
Definition locale.cpp:662
_TEST_EXPORT int rw_create_catalog(const char *catname, const char *catalog)
Definition locale.cpp:920
_TEST_EXPORT const char *const & rw_opt_locales
Definition locale.cpp:862
#define SLASH
Definition locale.cpp:110
_TEST_EXPORT size_t rw_get_mb_chars(rw_mbchar_array_t mb_chars)
Definition locale.cpp:618
_TEST_EXPORT const char * rw_set_locale_root()
Definition locale.cpp:324
#define TOPDIR
Definition locale.cpp:102
_TEST_EXPORT int rw_opt_setlocales(int argc, char *argv[])
Definition locale.cpp:866
#define EXE_SUFFIX
Definition locale.cpp:85
_TEST_EXPORT char * rw_locales(int loc_cat, const char *grep_exp, bool prepend_c_loc)
Definition locale.cpp:368
#define _QUIET_FREE(p)
#define RELPATH
Definition locale.cpp:119
char _rw_toupper(char ch)
Definition locale.cpp:974
_TEST_EXPORT const char * rw_find_mb_locale(size_t *mb_cur_max, rw_mbchar_array_t mb_chars)
Definition locale.cpp:724
#define PATH_MAX
Definition locale.cpp:98
bool _rw_isspace(char ch)
Definition locale.cpp:968
_TEST_EXPORT int rw_locale(const char *args, const char *fname)
Definition locale.cpp:132
#define IS_ABSOLUTE_PATHNAME(path)
Definition locale.cpp:111