Re: [RFC] newlib/libc/include/langinfo.h: nl_langinfo enum off by one error causing pointer overwrite
Brian Inglis <[email protected]>
| Newsgroups | gmane.comp.lib.newlib |
|---|---|
| Organization | Systematic Software |
| Message-ID | <[email protected]> |
On 2024-08-22 02:07, Corinna Vinschen wrote:
> Hi Brian,
>
> On Aug 21 17:58, Brian Inglis wrote:
>> if __HAVE_LOCALE_INFO__ is defined, then _NL_MESSAGES_CODESET is defined
>> instead of _NL_CTYPE_CODESET
>
> There is no _NL_CTYPE_CODESET, only CODESET, and the value of
> _NL_MESSAGES_CODESET is what it is for backward compatibility.
>
> The values must not change.
What about the pointer positions in the array?
>> demonstration of pointer overwrite by nl_langinfo dump program generated
>> with additional langinfo.h hack after fix:
>
> Can you please provide your STC?
Attached, with log, assuming current langinfo.h.
Also attached is my original program, for use with appropriately fixed and/or
hacked up langinfo.h, with my nl_items label array adjusted to match.
>> if __HAVE_LOCALE_INFO__ is not defined, then _NL_MESSAGES_CODESET is
>> also not defined, so it is unclear if _NL_MESSAGES_CODESET should be
>> defined to _NL_CTYPE_CODESET if neither __HAVE_LOCALE_INFO__ nor
>> __HAVE_LOCALE_INFO_EXTENDED__ are defined, or added as another field
>> depending on those definitions
>
> As I wrote above, the values must not change. And given _NL_CTYPE_CODESET
> doesn't really exist (but CODESET does), I'm not sure I understand the
> problem here. As on Linux we have a value _NL_CTYPE_CODESET_NAME which
> is equivalent to CODESET.
An instance of a CODESET item and pointer exists, before the wide messages
fields, and not accounted for in the enum defined in langinfo.h, used to
retrieve the values by nl_langinfo(3).
What is shown in the enum as the _NL_MESSAGE_CODESET item appears to be
generated from the LC_CTYPE locale entry, and added to the buffer and pointer
array after the _NL_CTYPE_MB_CUR_MAX pointer, before the _NL_CTYPE_OUTDIGIT?_MB
item pointers.
What is not shown in the enum is the CODESET item generated from the LC_MESSAGES
locale entry, and added to the buffer and pointer array after the
_NL_MONETARY_WNEGATIVE_SIGN pointer before the _NL_MESSAGES_WYESEXPR pointer.
As a result, the underlying pointers in the pointer array are off by one in the
wide messages fields, and the _NL_COLLATE_CODESET pointer clobbers what was
stored in the _NL_MESSAGES_WNOSTR position in the pointer array.
This is shown below from the STC log attached: you can see in the dump that the
underlying data from the current WNOSTR item, has L"yes" followed by L"no" L"",
and the following COLLATE_CODESET item looks normal:
MESSAGES_WYESEXPR 呕ⵆ8孞ㄫ她佯]孞〭乮]敹s潮帀嬀⬀礀夀漀伀崀
5455 '呕' 2d46 'ⵆ' 0038 '8' 5b5e '孞' 312b 'ㄫ' 5979 '她' 4f6f '佯' 005d ']'
55 'U' 54 'T' 46 'F' 2d '-' 38 '8' 00 '' 5e '^' 5b '[' 2b '+' 31 '1' 79 'y' 59
'Y' 6f 'o' 4f 'O' 5d ']' 00 ''
MESSAGES_WNOEXPR ^[+1yYoO]
005e '^' 005b '[' 002b '+' 0031 '1' 0079 'y' 0059 'Y' 006f 'o' 004f 'O'
5e '^' 00 '' 5b '[' 00 '' 2b '+' 00 '' 31 '1' 00 '' 79 'y' 00 '' 59 'Y' 00 '' 6f
'o' 00 '' 4f 'O' 00 ''
MESSAGES_WYESSTR ^[-0nN]
005e '^' 005b '[' 002d '-' 0030 '0' 006e 'n' 004e 'N' 005d ']' 0000 ''
5e '^' 00 '' 5b '[' 00 '' 2d '-' 00 '' 30 '0' 00 '' 6e 'n' 00 '' 4e 'N' 00 '' 5d
']' 00 '' 00 '' 00 ''
MESSAGES_WNOSTR yes
0079 'y' 0065 'e' 0073 's' 0000 '' 006e 'n' 006f 'o' 0000 '' 0000 ''
79 'y' 00 '' 65 'e' 00 '' 73 's' 00 '' 00 '' 00 '' 6e 'n' 00 '' 6f 'o' 00 '' 00
'' 00 '' 00 '' 00 ''
COLLATE_CODESET UTF-8
5455 '呕' 2d46 'ⵆ' 0038 '8' 0000 '' 0000 '' 0000 '' 0000 '' 0000 ''
55 'U' 54 'T' 46 'F' 2d '-' 38 '8' 00 '' 00 '' 00 '' 00 '' 00 '' 00 '' 00 '' 00
'' 00 '' 00 '' 00 ''
So the generated locale data contains a (message) codeset preceding the wide
message fields, and this is missing from the enum, so that the following item
pointers are off by one, and the collate codeset pointer and item look correct,
as the pointer clobbered that for WNOSTR.
Additional langinfo.h hack after fix to show existing problem:
$ diff -4 /usr{/src/newlib-cygwin/newlib/libc,}/include/langinfo.h
--- /usr/src/newlib-cygwin/newlib/libc/include/langinfo.h 2024-08-21
17:11:00.852069900 -0600
+++ /usr/include/langinfo.h 2024-08-21 16:59:59.609998900 -0600
@@ -301,9 +301,9 @@ enum
_NL_MESSAGES_CODESET,
_NL_MESSAGES_WYESEXPR,
_NL_MESSAGES_WNOEXPR,
_NL_MESSAGES_WYESSTR,
- _NL_MESSAGES_WNOSTR,
+#define _NL_MESSAGES_WNOSTR _NL_MESSAGES_WYESSTR
_NL_COLLATE_CODESET,
Demonstration of pointer overwrite by nl_langinfo dump program run with patch
and above hack:
MESSAGES_CODESET UTF-8
MESSAGES_WYESEXPR ^[+1yY]
MESSAGES_WNOEXPR ^[-0nN]
MESSAGES_WYESSTR yes
79 y 65 e 73 s 0 ^@ 6e n 6f o 0 ^@ 0 ^@ # %hx %C dump
COLLATE_CODESET UTF-8
If __HAVE_LOCALE_INFO__ is not defined, then _NL_MESSAGES_CODESET is
also not defined, so it is unclear if _NL_MESSAGES_CODESET should be
defined to _NL_CTYPE_CODESET if neither __HAVE_LOCALE_INFO__ nor
__HAVE_LOCALE_INFO_EXTENDED__ are defined, or added as another field
depending on those definitions?
--
Take care. Thanks, Brian Inglis Calgary, Alberta, Canada
La perfection est atteinte Perfection is achieved
non pas lorsqu'il n'y a plus rien à ajouter not when there is no more to add
mais lorsqu'il n'y a plus rien à retirer but when there is no more to cut
-- Antoine de Saint-Exupéry
langinfo-enum-missing-codeset-pointer-clobber-stc.c
(text/plain, 5.2 KB)
/*!/usr/bin/gcc -pipe -Wp,-D_FORTIFY_SOURCE=3 -fanalyzer -fsanitize-recover=all
* -fstack-check -fstack-protector-all --param=ssp-buffer-size=4
* -ggdb -Og -Wall -Wextra -Wformat=2 -Wformat-overflow=2
* -Werror=format-security
* langinfo-enum-missing-codeset-pointer-clobber-stc.c - show missing langinfo enum entry
*/
#define _GNU_SOURCE
#include <locale.h>
#include <langinfo.h>
#include <stdio.h>
static const char *nl_items[] =
{
"CODESET",
"D_T_FMT", /* string for formatting date and time */
"D_FMT", /* date format string */
"T_FMT", /* time format string */
"T_FMT_AMPM", /* a.m. or p.m. time formatting string */
"AM_STR", /* Ante Meridian affix */
"PM_STR", /* Post Meridian affix */
/* week day names */
"DAY_1",
"DAY_2",
"DAY_3",
"DAY_4",
"DAY_5",
"DAY_6",
"DAY_7",
/* abbreviated week day names */
"ABDAY_1",
"ABDAY_2",
"ABDAY_3",
"ABDAY_4",
"ABDAY_5",
"ABDAY_6",
"ABDAY_7",
/* month names */
"MON_1",
"MON_2",
"MON_3",
"MON_4",
"MON_5",
"MON_6",
"MON_7",
"MON_8",
"MON_9",
"MON_10",
"MON_11",
"MON_12",
/* abbreviated month names */
"ABMON_1",
"ABMON_2",
"ABMON_3",
"ABMON_4",
"ABMON_5",
"ABMON_6",
"ABMON_7",
"ABMON_8",
"ABMON_9",
"ABMON_10",
"ABMON_11",
"ABMON_12",
"ERA", /* era description segments */
"ERA_D_FMT", /* era date format string */
"ERA_D_T_FMT", /* era date and time format string */
"ERA_T_FMT", /* era time format string */
"ALT_DIGITS", /* alternative symbols for digits */
"RADIXCHAR", /* radix char */
"THOUSEP", /* separator for thousands */
"YESEXPR", /* affirmative response expression */
"NOEXPR", /* negative response expression */
"YESSTR", /* affirmative response for yes/no queries */
"NOSTR", /* negative response for yes/no queries */
"CRNCYSTR", /* currency symbol */
"D_MD_ORDER", /* month/day order (BSD extension) */
[84] = "DATE_FMT", /* date fmt used by date(1) (GNU extension) */
"MB_CUR_MAX", /* char value */
"MESSAGES_CODESET",
/* "CTYPE_CODESET", */
"LOCALE_EXTENDED_FIRST_ENTRY",
"OUTDIGIT0_MB",
"OUTDIGIT1_MB",
"OUTDIGIT2_MB",
"OUTDIGIT3_MB",
"OUTDIGIT4_MB",
"OUTDIGIT5_MB",
"OUTDIGIT6_MB",
"OUTDIGIT7_MB",
"OUTDIGIT8_MB",
"OUTDIGIT9_MB",
"OUTDIGIT0_WC",
"OUTDIGIT1_WC",
"OUTDIGIT2_WC",
"OUTDIGIT3_WC",
"OUTDIGIT4_WC",
"OUTDIGIT5_WC",
"OUTDIGIT6_WC",
"OUTDIGIT7_WC",
"OUTDIGIT8_WC",
"OUTDIGIT9_WC",
"TIME_CODESET",
"WMON_1",
"WMON_2",
"WMON_3",
"WMON_4",
"WMON_5",
"WMON_6",
"WMON_7",
"WMON_8",
"WMON_9",
"WMON_10",
"WMON_11",
"WMON_12",
"WMONTH_1",
"WMONTH_2",
"WMONTH_3",
"WMONTH_4",
"WMONTH_5",
"WMONTH_6",
"WMONTH_7",
"WMONTH_8",
"WMONTH_9",
"WMONTH_10",
"WMONTH_11",
"WMONTH_12",
"WWDAY_1",
"WWDAY_2",
"WWDAY_3",
"WWDAY_4",
"WWDAY_5",
"WWDAY_6",
"WWDAY_7",
"WWEEKDAY_1",
"WWEEKDAY_2",
"WWEEKDAY_3",
"WWEEKDAY_4",
"WWEEKDAY_5",
"WWEEKDAY_6",
"WWEEKDAY_7",
"WT_FMT",
"WD_FMT",
"WD_T_FMT",
"WAM_STR",
"WPM_STR",
"WDATE_FMT",
"WT_FMT_AMPM",
"WERA",
"WERA_D_FMT",
"WERA_D_T_FMT",
"WERA_T_FMT",
"WALT_DIGITS",
"NUMERIC_CODESET",
"NUMERIC_GROUPING",
"NUMERIC_DECIMAL_POINT_WC",
"NUMERIC_THOUSANDS_SEP_WC",
"MONETARY_INT_CURR_SYMBOL",
"MONETARY_CURRENCY_SYMBOL",
"MONETARY_MON_DECIMAL_POINT",
"MONETARY_MON_THOUSANDS_SEP",
"MONETARY_MON_GROUPING",
"MONETARY_POSITIVE_SIGN",
"MONETARY_NEGATIVE_SIGN",
"MONETARY_INT_FRAC_DIGITS",
"MONETARY_FRAC_DIGITS",
"MONETARY_P_CS_PRECEDES",
"MONETARY_P_SEP_BY_SPACE",
"MONETARY_N_CS_PRECEDES",
"MONETARY_N_SEP_BY_SPACE",
"MONETARY_P_SIGN_POSN",
"MONETARY_N_SIGN_POSN",
"MONETARY_INT_P_CS_PRECEDES",
"MONETARY_INT_P_SEP_BY_SPACE",
"MONETARY_INT_N_CS_PRECEDES",
"MONETARY_INT_N_SEP_BY_SPACE",
"MONETARY_INT_P_SIGN_POSN",
"MONETARY_INT_N_SIGN_POSN",
"MONETARY_CODESET",
"MONETARY_WINT_CURR_SYMBOL",
"MONETARY_WCURRENCY_SYMBOL",
"MONETARY_WMON_DECIMAL_POINT",
"MONETARY_WMON_THOUSANDS_SEP",
"MONETARY_WPOSITIVE_SIGN",
"MONETARY_WNEGATIVE_SIGN",
/* "MESSAGES_CODESET", */
"MESSAGES_WYESEXPR",
"MESSAGES_WNOEXPR",
"MESSAGES_WYESSTR",
"MESSAGES_WNOSTR",
"COLLATE_CODESET",
"LOCALE_EXTENDED_LAST_ENTRY",
};
int
main (void)
{
char *locale = setlocale (LC_ALL, "");
struct lconv *lconv = localeconv ();
for (nl_item ni = _NL_MESSAGES_WYESEXPR; ni <= _NL_COLLATE_CODESET; ++ni)
{
char *item = nl_langinfo (ni);
if (item && *item)
{
if (_NL_MESSAGES_WYESEXPR <= ni && ni <= _NL_MESSAGES_WNOSTR)
{
printf ("%-32s %S\n", nl_items[ni], (wchar_t *)item);
} else {
printf ("%-32s %s\n", nl_items[ni], item);
}
for (wchar_t *g = (wchar_t *)item; g < (wchar_t *)item + 8; ++g)
{
printf ("%04hx '%C' ", *g, *g);
}
printf ("\n");
for (char *g = item; g < item + 16; ++g)
{
printf ("%02hhx '%c' ", *g, *g);
}
printf ("\n");
}
}
}
langinfo-enum-missing-codeset-pointer-clobber-stc.log
(application/octet-stream, 1.2 KB) - not displayed
locale_langinfo.c
(text/plain, 11.5 KB)
/*!/usr/bin/gcc -pipe -Wp,-D_FORTIFY_SOURCE=3 -fanalyzer -fsanitize-recover=all
* -fstack-check -fstack-protector-all --param=ssp-buffer-size=4
* -ggdb -Og -Wall -Wextra -Wformat=2 -Wformat-overflow=2
* -Werror=format-security
* locale_langinfo.c - show values for formatting numeric and monetary
* quantities, dates/times, and messages
*/
#define _GNU_SOURCE
#include <locale.h>
#include <langinfo.h>
#include <stdio.h>
static const char *nl_items[] =
{
"CODESET",
"D_T_FMT", /* string for formatting date and time */
"D_FMT", /* date format string */
"T_FMT", /* time format string */
"T_FMT_AMPM", /* a.m. or p.m. time formatting string */
"AM_STR", /* Ante Meridian affix */
"PM_STR", /* Post Meridian affix */
/* week day names */
"DAY_1",
"DAY_2",
"DAY_3",
"DAY_4",
"DAY_5",
"DAY_6",
"DAY_7",
/* abbreviated week day names */
"ABDAY_1",
"ABDAY_2",
"ABDAY_3",
"ABDAY_4",
"ABDAY_5",
"ABDAY_6",
"ABDAY_7",
/* month names */
"MON_1",
"MON_2",
"MON_3",
"MON_4",
"MON_5",
"MON_6",
"MON_7",
"MON_8",
"MON_9",
"MON_10",
"MON_11",
"MON_12",
/* abbreviated month names */
"ABMON_1",
"ABMON_2",
"ABMON_3",
"ABMON_4",
"ABMON_5",
"ABMON_6",
"ABMON_7",
"ABMON_8",
"ABMON_9",
"ABMON_10",
"ABMON_11",
"ABMON_12",
"ERA", /* era description segments */
"ERA_D_FMT", /* era date format string */
"ERA_D_T_FMT", /* era date and time format string */
"ERA_T_FMT", /* era time format string */
"ALT_DIGITS", /* alternative symbols for digits */
"RADIXCHAR", /* radix char */
"THOUSEP", /* separator for thousands */
"YESEXPR", /* affirmative response expression */
"NOEXPR", /* negative response expression */
"YESSTR", /* affirmative response for yes/no queries */
"NOSTR", /* negative response for yes/no queries */
"CRNCYSTR", /* currency symbol */
"D_MD_ORDER", /* month/day order (BSD extension) */
[84] = "DATE_FMT", /* date fmt used by date(1) (GNU extension) */
"MB_CUR_MAX", /* char value */
"CTYPE_CODESET",
"LOCALE_EXTENDED_FIRST_ENTRY",
"OUTDIGIT0_MB",
"OUTDIGIT1_MB",
"OUTDIGIT2_MB",
"OUTDIGIT3_MB",
"OUTDIGIT4_MB",
"OUTDIGIT5_MB",
"OUTDIGIT6_MB",
"OUTDIGIT7_MB",
"OUTDIGIT8_MB",
"OUTDIGIT9_MB",
"OUTDIGIT0_WC",
"OUTDIGIT1_WC",
"OUTDIGIT2_WC",
"OUTDIGIT3_WC",
"OUTDIGIT4_WC",
"OUTDIGIT5_WC",
"OUTDIGIT6_WC",
"OUTDIGIT7_WC",
"OUTDIGIT8_WC",
"OUTDIGIT9_WC",
"TIME_CODESET",
"WMON_1",
"WMON_2",
"WMON_3",
"WMON_4",
"WMON_5",
"WMON_6",
"WMON_7",
"WMON_8",
"WMON_9",
"WMON_10",
"WMON_11",
"WMON_12",
"WMONTH_1",
"WMONTH_2",
"WMONTH_3",
"WMONTH_4",
"WMONTH_5",
"WMONTH_6",
"WMONTH_7",
"WMONTH_8",
"WMONTH_9",
"WMONTH_10",
"WMONTH_11",
"WMONTH_12",
"WWDAY_1",
"WWDAY_2",
"WWDAY_3",
"WWDAY_4",
"WWDAY_5",
"WWDAY_6",
"WWDAY_7",
"WWEEKDAY_1",
"WWEEKDAY_2",
"WWEEKDAY_3",
"WWEEKDAY_4",
"WWEEKDAY_5",
"WWEEKDAY_6",
"WWEEKDAY_7",
"WT_FMT",
"WD_FMT",
"WD_T_FMT",
"WAM_STR",
"WPM_STR",
"WDATE_FMT",
"WT_FMT_AMPM",
"WERA",
"WERA_D_FMT",
"WERA_D_T_FMT",
"WERA_T_FMT",
"WALT_DIGITS",
"NUMERIC_CODESET",
"NUMERIC_GROUPING",
"NUMERIC_DECIMAL_POINT_WC",
"NUMERIC_THOUSANDS_SEP_WC",
"MONETARY_INT_CURR_SYMBOL",
"MONETARY_CURRENCY_SYMBOL",
"MONETARY_MON_DECIMAL_POINT",
"MONETARY_MON_THOUSANDS_SEP",
"MONETARY_MON_GROUPING",
"MONETARY_POSITIVE_SIGN",
"MONETARY_NEGATIVE_SIGN",
"MONETARY_INT_FRAC_DIGITS",
"MONETARY_FRAC_DIGITS",
"MONETARY_P_CS_PRECEDES",
"MONETARY_P_SEP_BY_SPACE",
"MONETARY_N_CS_PRECEDES",
"MONETARY_N_SEP_BY_SPACE",
"MONETARY_P_SIGN_POSN",
"MONETARY_N_SIGN_POSN",
"MONETARY_INT_P_CS_PRECEDES",
"MONETARY_INT_P_SEP_BY_SPACE",
"MONETARY_INT_N_CS_PRECEDES",
"MONETARY_INT_N_SEP_BY_SPACE",
"MONETARY_INT_P_SIGN_POSN",
"MONETARY_INT_N_SIGN_POSN",
"MONETARY_CODESET",
"MONETARY_WINT_CURR_SYMBOL",
"MONETARY_WCURRENCY_SYMBOL",
"MONETARY_WMON_DECIMAL_POINT",
"MONETARY_WMON_THOUSANDS_SEP",
"MONETARY_WPOSITIVE_SIGN",
"MONETARY_WNEGATIVE_SIGN",
"MESSAGES_CODESET",
"MESSAGES_WYESEXPR",
"MESSAGES_WNOEXPR",
"MESSAGES_WYESSTR",
"COLLATE_CODESET",
"MESSAGES_WNOSTR",
"LOCALE_EXTENDED_LAST_ENTRY",
};
static const char *locale_categories[] =
{
"LC_ALL",
"LC_COLLATE",
"LC_CTYPE",
"LC_MONETARY",
"LC_NUMERIC",
"LC_TIME",
"LC_MESSAGES",
};
int
main (void)
{
/*
* LC_ALL_MASK
* LC_COLLATE_MASK
* LC_CTYPE_MASK
* LC_MONETARY_MASK
* LC_NUMERIC_MASK
* LC_TIME_MASK
* LC_MESSAGES_MASK
*
* LC_GLOBAL_LOCALE
*/
char * locale = setlocale (LC_ALL, "");
struct lconv * lconv = localeconv ();
/*
* The elements of grouping and mon_grouping are interpreted according to the following:
* {CHAR_MAX} No further grouping is to be performed.
* 0 The previous element is to be repeatedly used for the remainder of the digits.
* other The integer value is the number of digits that comprise the current group. The next element is exam‐
* ined to determine the size of the next group of digits before the current group.
* The values of p_sep_by_space, n_sep_by_space, int_p_sep_by_space, and int_n_sep_by_space are interpreted accord‐
* ing to the following:
* 0 No space separates the currency symbol and value.
* 1 If the currency symbol and sign string are adjacent, a space separates them from the value; otherwise, a
* space separates the currency symbol from the value.
* 2 If the currency symbol and sign string are adjacent, a space separates them; otherwise, a space separates
* the sign string from the value.
* For int_p_sep_by_space and int_n_sep_by_space, the fourth character of int_curr_symbol is used instead of a
* space.
* The values of p_sign_posn, n_sign_posn, int_p_sign_posn, and int_n_sign_posn are interpreted according to the
* following:
* 0 Parentheses surround the quantity and currency_symbol or int_curr_symbol.
* 1 The sign string precedes the quantity and currency_symbol or int_curr_symbol.
* 2 The sign string succeeds the quantity and currency_symbol or int_curr_symbol.
* 3 The sign string immediately precedes the currency_symbol or int_curr_symbol.
* 4 The sign string immediately succeeds the currency_symbol or int_curr_symbol.
*/
printf ("locale '%s' conv numeric "
" >0 '%s' <0 '%s'"
" group ",
locale,
lconv->positive_sign, lconv->negative_sign);
for (char * g = lconv->grouping; /*BREAK*/; ++g)
{
printf ("%hhx ", *g);
if (!*g || ~1 == *g) break;
}
printf (" sep %s frac %s\n"
" >0 posn %hhx before %hhx sp %hhx "
" <0 posn %hhx before %hhx sp %hhx\n"
" money '%s'"
" group ",
lconv->thousands_sep, lconv->decimal_point,
lconv->p_sign_posn, lconv->p_cs_precedes, lconv->p_sep_by_space,
lconv->n_sign_posn, lconv->n_cs_precedes, lconv->n_sep_by_space,
lconv->currency_symbol);
for (char * g = lconv->mon_grouping; /*BREAK*/; ++g)
{
printf ("%hhx ", *g);
if (!*g || ~1 == *g) break;
}
printf (" sep %s frac %s"
" digits %hhx\n"
" intl '%s'"
" >0 posn %hhx before %hhx sp %hhx "
" <0 posn %hhx before %hhx sp %hhx "
" digits %hhx\n",
lconv->mon_thousands_sep, lconv->mon_decimal_point, lconv->frac_digits,
lconv->int_curr_symbol,
lconv->int_p_sign_posn, lconv->int_p_cs_precedes, lconv->int_p_sep_by_space,
lconv->int_n_sign_posn, lconv->int_n_cs_precedes, lconv->int_n_sep_by_space,
lconv->int_frac_digits);
for (int lc = LC_COLLATE; lc <= LC_MESSAGES; ++lc)
{
char *item = nl_langinfo (NL_LOCALE_NAME(lc));
printf ("%-12s '%s'\n", locale_categories[lc], item);
}
for (nl_item ni = CODESET; ni < _NL_LOCALE_EXTENDED_LAST_ENTRY; ++ni)
{
char *item = nl_langinfo (ni);
if (item && *item)
{
if (*item >= ' ')
{
if ((_NL_CTYPE_OUTDIGIT0_WC <= ni &&
ni <= _NL_CTYPE_OUTDIGIT9_WC)
|| (_NL_TIME_WMON_1 <= ni &&
ni <= _NL_TIME_WALT_DIGITS)
|| (_NL_MONETARY_WINT_CURR_SYMBOL <= ni &&
ni <= _NL_MONETARY_WNEGATIVE_SIGN)
|| (_NL_MESSAGES_WYESEXPR <= ni &&
ni <= _NL_MESSAGES_WNOSTR))
{
printf ("%-32s %S\n", nl_items[ni], item);
if (_NL_MESSAGES_WNOSTR == ni)
{
for (wchar_t * g = item; g < (wchar_t *)item + 8; ++g)
{
printf ("%hx %C ", *g, *g);
}
printf ("\n");
}
} else {
printf ("%-32s %s\n", nl_items[ni], item);
}
} else {
printf ("%-32s ", nl_items[ni]);
for (char * g = item; /*BREAK*/; ++g)
{
printf ("%hhx ", *g);
/* single character for monetary flags */
if (_NL_MONETARY_INT_FRAC_DIGITS <= ni &&
ni <= _NL_MONETARY_INT_N_SIGN_POSN) break;
/* one or more characters for grouping etc */
if (!*g || ~1 == *g) break;
}
printf ("\n");
}
}
}
}
#if 0
_NL_CTYPE_CODESET_NAME = 0, /* codeset name */
#define CODESET _NL_CTYPE_CODESET_NAME
...
_NL_TIME_DATE_FMT = 84, /* date fmt used by date(1) (GNU extension) */
#define _DATE_FMT _NL_TIME_DATE_FMT
_NL_CTYPE_MB_CUR_MAX,
_NL_MESSAGES_CODESET,
_NL_LOCALE_EXTENDED_FIRST_ENTRY,
_NL_CTYPE_OUTDIGIT0_MB,
_NL_CTYPE_OUTDIGIT1_MB,
_NL_CTYPE_OUTDIGIT2_MB,
_NL_CTYPE_OUTDIGIT3_MB,
_NL_CTYPE_OUTDIGIT4_MB,
_NL_CTYPE_OUTDIGIT5_MB,
_NL_CTYPE_OUTDIGIT6_MB,
_NL_CTYPE_OUTDIGIT7_MB,
_NL_CTYPE_OUTDIGIT8_MB,
_NL_CTYPE_OUTDIGIT9_MB,
_NL_CTYPE_OUTDIGIT0_WC,
_NL_CTYPE_OUTDIGIT1_WC,
_NL_CTYPE_OUTDIGIT2_WC,
_NL_CTYPE_OUTDIGIT3_WC,
_NL_CTYPE_OUTDIGIT4_WC,
_NL_CTYPE_OUTDIGIT5_WC,
_NL_CTYPE_OUTDIGIT6_WC,
_NL_CTYPE_OUTDIGIT7_WC,
_NL_CTYPE_OUTDIGIT8_WC,
_NL_CTYPE_OUTDIGIT9_WC,
_NL_TIME_CODESET,
_NL_TIME_WMON_1,
_NL_TIME_WMON_2,
_NL_TIME_WMON_3,
_NL_TIME_WMON_4,
_NL_TIME_WMON_5,
_NL_TIME_WMON_6,
_NL_TIME_WMON_7,
_NL_TIME_WMON_8,
_NL_TIME_WMON_9,
_NL_TIME_WMON_10,
_NL_TIME_WMON_11,
_NL_TIME_WMON_12,
_NL_TIME_WMONTH_1,
_NL_TIME_WMONTH_2,
_NL_TIME_WMONTH_3,
_NL_TIME_WMONTH_4,
_NL_TIME_WMONTH_5,
_NL_TIME_WMONTH_6,
_NL_TIME_WMONTH_7,
_NL_TIME_WMONTH_8,
_NL_TIME_WMONTH_9,
_NL_TIME_WMONTH_10,
_NL_TIME_WMONTH_11,
_NL_TIME_WMONTH_12,
_NL_TIME_WWDAY_1,
_NL_TIME_WWDAY_2,
_NL_TIME_WWDAY_3,
_NL_TIME_WWDAY_4,
_NL_TIME_WWDAY_5,
_NL_TIME_WWDAY_6,
_NL_TIME_WWDAY_7,
_NL_TIME_WWEEKDAY_1,
_NL_TIME_WWEEKDAY_2,
_NL_TIME_WWEEKDAY_3,
_NL_TIME_WWEEKDAY_4,
_NL_TIME_WWEEKDAY_5,
_NL_TIME_WWEEKDAY_6,
_NL_TIME_WWEEKDAY_7,
_NL_TIME_WT_FMT,
_NL_TIME_WD_FMT,
_NL_TIME_WD_T_FMT,
_NL_TIME_WAM_STR,
_NL_TIME_WPM_STR,
_NL_TIME_WDATE_FMT,
_NL_TIME_WT_FMT_AMPM,
_NL_TIME_WERA,
_NL_TIME_WERA_D_FMT,
_NL_TIME_WERA_D_T_FMT,
_NL_TIME_WERA_T_FMT,
_NL_TIME_WALT_DIGITS,
...
_NL_MONETARY_INT_FRAC_DIGITS,
_NL_MONETARY_FRAC_DIGITS,
_NL_MONETARY_P_CS_PRECEDES,
_NL_MONETARY_P_SEP_BY_SPACE,
_NL_MONETARY_N_CS_PRECEDES,
_NL_MONETARY_N_SEP_BY_SPACE,
_NL_MONETARY_P_SIGN_POSN,
_NL_MONETARY_N_SIGN_POSN,
_NL_MONETARY_INT_P_CS_PRECEDES,
_NL_MONETARY_INT_P_SEP_BY_SPACE,
_NL_MONETARY_INT_N_CS_PRECEDES,
_NL_MONETARY_INT_N_SEP_BY_SPACE,
_NL_MONETARY_INT_P_SIGN_POSN,
_NL_MONETARY_INT_N_SIGN_POSN,
...
_NL_LOCALE_EXTENDED_LAST_ENTRY
_NL_LOCALE_NAME_BASE 100000
#endif