0001-localedef-Update-LC_MONETARY-handling-Bug-28845.patch 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. From 3feecd80013c822a12d4b01c5c25e155dfbc6e2f Mon Sep 17 00:00:00 2001
  2. From: Carlos O'Donell <carlos@redhat.com>
  3. Date: Thu, 3 Feb 2022 16:51:59 -0500
  4. Subject: [PATCH] localedef: Update LC_MONETARY handling (Bug 28845)
  5. ISO C17, POSIX Issue 7, and ISO 30112 all allow the char*
  6. types to be empty strings i.e. "", integer or char values to
  7. be -1 or CHAR_MAX respectively, with the exception of
  8. decimal_point which must be non-empty in ISO C. Note that
  9. the defaults for mon_grouping vary, but are functionaly
  10. equivalent e.g. "\177" (no further grouping reuqired) vs.
  11. "" (no grouping defined for all groups).
  12. We include a broad comment talking about harmonizing ISO C,
  13. POSIX, ISO 30112, and the default C/POSIX locale for glibc.
  14. We reorder all setting based on locale/categories.def order.
  15. We soften all missing definitions from errors to warnings when
  16. defaults exist.
  17. Given that ISO C, POSIX and ISO 30112 allow the empty string
  18. we change LC_MONETARY handling of mon_decimal_point to allow
  19. the empty string. If mon_decimal_point is not defined at all
  20. then we pick the existing legacy glibc default value of
  21. <U002E> i.e. ".".
  22. We also set the default for mon_thousands_sep_wc at the
  23. same time as mon_thousands_sep, but this is not a change in
  24. behaviour, it is always either a matching value or L'\0',
  25. but if in the future we change the default to a non-empty
  26. string we would need to update both at the same time.
  27. Tested on x86_64 and i686 without regressions.
  28. Tested with install-locale-archive target.
  29. Tested with install-locale-files target.
  30. Reviewed-by: DJ Delorie <dj@redhat.com>
  31. (cherry picked from commit 2ab8b74567dc0a9a3c98696e6444881997dd6c49)
  32. ---
  33. locale/programs/ld-monetary.c | 182 +++++++++++++++++++++++++++-------
  34. 1 file changed, 146 insertions(+), 36 deletions(-)
  35. diff --git a/locale/programs/ld-monetary.c b/locale/programs/ld-monetary.c
  36. index 3b0412b405..18698bbe94 100644
  37. --- a/locale/programs/ld-monetary.c
  38. +++ b/locale/programs/ld-monetary.c
  39. @@ -196,21 +196,105 @@ No definition for %s category found"), "LC_MONETARY");
  40. }
  41. }
  42. + /* Generally speaking there are 3 standards the define the default,
  43. + warning, and error behaviour of LC_MONETARY. They are ISO/IEC TR 30112,
  44. + ISO/IEC 9899:2018 (ISO C17), and POSIX.1-2017. Within 30112 we have the
  45. + definition of a standard i18n FDCC-set, which for LC_MONETARY has the
  46. + following default values:
  47. + int_curr_symbol ""
  48. + currency_symbol ""
  49. + mon_decimal_point "<U002C>" i.e. ","
  50. + mon_thousand_sep ""
  51. + mon_grouping "\177" i.e. CHAR_MAX
  52. + positive_sign ""
  53. + negative_sign "<U002E>" i.e. "."
  54. + int_frac_digits -1
  55. + frac_digits -1
  56. + p_cs_precedes -1
  57. + p_sep_by_space -1
  58. + n_cs_precedes -1
  59. + n_sep_by_space -1
  60. + p_sign_posn -1
  61. + n_sign_posn -1
  62. + Under 30112 a keyword that is not provided implies an empty string ""
  63. + for string values or a -1 for integer values, and indicates the value
  64. + is unspecified with no default implied. No errors are considered.
  65. + The exception is mon_grouping which is a string with a terminating
  66. + CHAR_MAX.
  67. + For POSIX Issue 7 we have:
  68. + https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap07.html
  69. + and again values not provided default to "" or -1, and indicate the value
  70. + is not available to the locale. The exception is mon_grouping which is
  71. + a string with a terminating CHAR_MAX. For the POSIX locale the values of
  72. + LC_MONETARY should be:
  73. + int_curr_symbol ""
  74. + currency_symbol ""
  75. + mon_decimal_point ""
  76. + mon_thousands_sep ""
  77. + mon_grouping "\177" i.e. CHAR_MAX
  78. + positive_sign ""
  79. + negative_sign ""
  80. + int_frac_digits -1
  81. + frac_digits -1
  82. + p_cs_precedes -1
  83. + p_sep_by_space -1
  84. + n_cs_precedes -1
  85. + n_sep_by_space -1
  86. + p_sign_posn -1
  87. + n_sign_posn -1
  88. + int_p_cs_precedes -1
  89. + int_p_sep_by_space -1
  90. + int_n_cs_precedes -1
  91. + int_n_sep_by_space -1
  92. + int_p_sign_posn -1
  93. + int_n_sign_posn -1
  94. + Like with 30112, POSIX also considers no error if the keywords are
  95. + missing, only that if the cateory as a whole is missing the referencing
  96. + of the category results in unspecified behaviour.
  97. + For ISO C17 there is no default value provided, but the localeconv
  98. + specification in 7.11.2.1 admits that members of char * type may point
  99. + to "" to indicate a value is not available or is of length zero.
  100. + The exception is decimal_point (not mon_decimal_point) which must be a
  101. + defined non-empty string. The values of char, which are generally
  102. + mapped to integer values in 30112 and POSIX, must be non-negative
  103. + numbers that map to CHAR_MAX when a value is not available in the
  104. + locale.
  105. + In ISO C17 for the "C" locale all values are empty strings "", or
  106. + CHAR_MAX, with the exception of decimal_point which is "." (defined
  107. + in LC_NUMERIC). ISO C17 makes no exception for mon_grouping like
  108. + 30112 and POSIX, but a value of "" is functionally equivalent to
  109. + "\177" since neither defines a grouping (though the latter terminates
  110. + the grouping).
  111. +
  112. + Lastly, we must consider the legacy C/POSIX locale that implemented
  113. + as a builtin in glibc and wether a default value mapping to the
  114. + C/POSIX locale may benefit the user from a compatibility perspective.
  115. +
  116. + Thus given 30112, POSIX, ISO C, and the builtin C/POSIX locale we
  117. + need to pick appropriate defaults below. */
  118. +
  119. + /* The members of LC_MONETARY are handled in the order of their definition
  120. + in locale/categories.def. Please keep them in that order. */
  121. +
  122. + /* The purpose of TEST_ELEM is to define a default value for the fields
  123. + in the category if the field was not defined in the cateory. If the
  124. + category was present but we didn't see a definition for the field then
  125. + we also issue a warning, otherwise the only warning you get is the one
  126. + earlier when a default category is created (completely missing category).
  127. + This missing field warning is glibc-specific since no standard requires
  128. + this warning, but we consider it valuable to print a warning for all
  129. + missing fields in the category. */
  130. #define TEST_ELEM(cat, initval) \
  131. if (monetary->cat == NULL) \
  132. { \
  133. if (! nothing) \
  134. - record_error (0, 0, _("%s: field `%s' not defined"), \
  135. - "LC_MONETARY", #cat); \
  136. + record_warning (_("%s: field `%s' not defined"), \
  137. + "LC_MONETARY", #cat); \
  138. monetary->cat = initval; \
  139. }
  140. + /* Keyword: int_curr_symbol. */
  141. TEST_ELEM (int_curr_symbol, "");
  142. - TEST_ELEM (currency_symbol, "");
  143. - TEST_ELEM (mon_thousands_sep, "");
  144. - TEST_ELEM (positive_sign, "");
  145. - TEST_ELEM (negative_sign, "");
  146. -
  147. /* The international currency symbol must come from ISO 4217. */
  148. if (monetary->int_curr_symbol != NULL)
  149. {
  150. @@ -247,41 +331,63 @@ not correspond to a valid name in ISO 4217 [--no-warnings=intcurrsym]"),
  151. }
  152. }
  153. - /* The decimal point must not be empty. This is not said explicitly
  154. - in POSIX but ANSI C (ISO/IEC 9899) says in 4.4.2.1 it has to be
  155. - != "". */
  156. + /* Keyword: currency_symbol */
  157. + TEST_ELEM (currency_symbol, "");
  158. +
  159. + /* Keyword: mon_decimal_point */
  160. + /* ISO C17 7.11.2.1.3 explicitly allows mon_decimal_point to be the
  161. + empty string e.g. "". This indicates the value is not available in the
  162. + current locale or is of zero length. However, if the value was never
  163. + defined then we issue a warning and use a glibc-specific default. ISO
  164. + 30112 in the i18n FDCC-Set uses <U002C> ",", and POSIX Issue 7 in the
  165. + POSIX locale uses "". It is specific to glibc that the default is <U002E>
  166. + "."; we retain this existing behaviour for backwards compatibility. */
  167. if (monetary->mon_decimal_point == NULL)
  168. {
  169. if (! nothing)
  170. - record_error (0, 0, _("%s: field `%s' not defined"),
  171. - "LC_MONETARY", "mon_decimal_point");
  172. + record_warning (_("%s: field `%s' not defined, using defaults"),
  173. + "LC_MONETARY", "mon_decimal_point");
  174. monetary->mon_decimal_point = ".";
  175. monetary->mon_decimal_point_wc = L'.';
  176. }
  177. - else if (monetary->mon_decimal_point[0] == '\0' && ! be_quiet && ! nothing)
  178. +
  179. + /* Keyword: mon_thousands_sep */
  180. + if (monetary->mon_thousands_sep == NULL)
  181. {
  182. - record_error (0, 0, _("\
  183. -%s: value for field `%s' must not be an empty string"),
  184. - "LC_MONETARY", "mon_decimal_point");
  185. + if (! nothing)
  186. + record_warning (_("%s: field `%s' not defined, using defaults"),
  187. + "LC_MONETARY", "mon_thousands_sep");
  188. + monetary->mon_thousands_sep = "";
  189. + monetary->mon_thousands_sep_wc = L'\0';
  190. }
  191. + /* Keyword: mon_grouping */
  192. if (monetary->mon_grouping_len == 0)
  193. {
  194. if (! nothing)
  195. - record_error (0, 0, _("%s: field `%s' not defined"),
  196. - "LC_MONETARY", "mon_grouping");
  197. -
  198. + record_warning (_("%s: field `%s' not defined"),
  199. + "LC_MONETARY", "mon_grouping");
  200. + /* Missing entries are given 1 element in their bytearray with
  201. + a value of CHAR_MAX which indicates that "No further grouping
  202. + is to be performed" (functionally equivalent to ISO C's "C"
  203. + locale default of ""). */
  204. monetary->mon_grouping = (char *) "\177";
  205. monetary->mon_grouping_len = 1;
  206. }
  207. + /* Keyword: positive_sign */
  208. + TEST_ELEM (positive_sign, "");
  209. +
  210. + /* Keyword: negative_sign */
  211. + TEST_ELEM (negative_sign, "");
  212. +
  213. #undef TEST_ELEM
  214. #define TEST_ELEM(cat, min, max, initval) \
  215. if (monetary->cat == -2) \
  216. { \
  217. if (! nothing) \
  218. - record_error (0, 0, _("%s: field `%s' not defined"), \
  219. - "LC_MONETARY", #cat); \
  220. + record_warning (_("%s: field `%s' not defined"), \
  221. + "LC_MONETARY", #cat); \
  222. monetary->cat = initval; \
  223. } \
  224. else if ((monetary->cat < min || monetary->cat > max) \
  225. @@ -300,16 +406,11 @@ not correspond to a valid name in ISO 4217 [--no-warnings=intcurrsym]"),
  226. TEST_ELEM (p_sign_posn, -1, 4, -1);
  227. TEST_ELEM (n_sign_posn, -1, 4, -1);
  228. - /* The non-POSIX.2 extensions are optional. */
  229. - if (monetary->duo_int_curr_symbol == NULL)
  230. - monetary->duo_int_curr_symbol = monetary->int_curr_symbol;
  231. - if (monetary->duo_currency_symbol == NULL)
  232. - monetary->duo_currency_symbol = monetary->currency_symbol;
  233. -
  234. - if (monetary->duo_int_frac_digits == -2)
  235. - monetary->duo_int_frac_digits = monetary->int_frac_digits;
  236. - if (monetary->duo_frac_digits == -2)
  237. - monetary->duo_frac_digits = monetary->frac_digits;
  238. + /* Keyword: crncystr */
  239. + monetary->crncystr = (char *) xmalloc (strlen (monetary->currency_symbol)
  240. + + 2);
  241. + monetary->crncystr[0] = monetary->p_cs_precedes ? '-' : '+';
  242. + strcpy (&monetary->crncystr[1], monetary->currency_symbol);
  243. #undef TEST_ELEM
  244. #define TEST_ELEM(cat, alt, min, max) \
  245. @@ -327,6 +428,17 @@ not correspond to a valid name in ISO 4217 [--no-warnings=intcurrsym]"),
  246. TEST_ELEM (int_p_sign_posn, p_sign_posn, -1, 4);
  247. TEST_ELEM (int_n_sign_posn, n_sign_posn, -1, 4);
  248. + /* The non-POSIX.2 extensions are optional. */
  249. + if (monetary->duo_int_curr_symbol == NULL)
  250. + monetary->duo_int_curr_symbol = monetary->int_curr_symbol;
  251. + if (monetary->duo_currency_symbol == NULL)
  252. + monetary->duo_currency_symbol = monetary->currency_symbol;
  253. +
  254. + if (monetary->duo_int_frac_digits == -2)
  255. + monetary->duo_int_frac_digits = monetary->int_frac_digits;
  256. + if (monetary->duo_frac_digits == -2)
  257. + monetary->duo_frac_digits = monetary->frac_digits;
  258. +
  259. TEST_ELEM (duo_p_cs_precedes, p_cs_precedes, -1, 1);
  260. TEST_ELEM (duo_p_sep_by_space, p_sep_by_space, -1, 2);
  261. TEST_ELEM (duo_n_cs_precedes, n_cs_precedes, -1, 1);
  262. @@ -349,17 +461,15 @@ not correspond to a valid name in ISO 4217 [--no-warnings=intcurrsym]"),
  263. if (monetary->duo_valid_to == 0)
  264. monetary->duo_valid_to = 99991231;
  265. + /* Keyword: conversion_rate */
  266. if (monetary->conversion_rate[0] == 0)
  267. {
  268. monetary->conversion_rate[0] = 1;
  269. monetary->conversion_rate[1] = 1;
  270. }
  271. - /* Create the crncystr entry. */
  272. - monetary->crncystr = (char *) xmalloc (strlen (monetary->currency_symbol)
  273. - + 2);
  274. - monetary->crncystr[0] = monetary->p_cs_precedes ? '-' : '+';
  275. - strcpy (&monetary->crncystr[1], monetary->currency_symbol);
  276. + /* A value for monetary-decimal-point-wc was set when
  277. + monetary_decimal_point was set, likewise for monetary-thousands-sep-wc. */
  278. }
  279. --
  280. 2.35.1