ld-numeric.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. /* Copyright (C) 1995-2026 Free Software Foundation, Inc.
  2. This file is part of the GNU C Library.
  3. This program is free software; you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published
  5. by the Free Software Foundation; version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program; if not, see <https://www.gnu.org/licenses/>. */
  13. #ifdef HAVE_CONFIG_H
  14. # include <config.h>
  15. #endif
  16. #include <langinfo.h>
  17. #include <string.h>
  18. #include <stdint.h>
  19. #include <sys/uio.h>
  20. #include <assert.h>
  21. #include "localedef.h"
  22. #include "linereader.h"
  23. #include "localeinfo.h"
  24. #include "locfile.h"
  25. /* The real definition of the struct for the LC_NUMERIC locale. */
  26. struct locale_numeric_t
  27. {
  28. const char *decimal_point;
  29. const char *thousands_sep;
  30. char *grouping;
  31. size_t grouping_len;
  32. uint32_t decimal_point_wc;
  33. uint32_t thousands_sep_wc;
  34. };
  35. static void
  36. numeric_startup (struct linereader *lr, struct localedef_t *locale,
  37. int ignore_content)
  38. {
  39. if (!ignore_content)
  40. {
  41. locale->categories[LC_NUMERIC].numeric =
  42. (struct locale_numeric_t *) xcalloc (1,
  43. sizeof (struct locale_numeric_t));
  44. }
  45. if (lr != NULL)
  46. {
  47. lr->translate_strings = 1;
  48. lr->return_widestr = 0;
  49. }
  50. }
  51. void
  52. numeric_finish (struct localedef_t *locale, const struct charmap_t *charmap)
  53. {
  54. struct locale_numeric_t *numeric = locale->categories[LC_NUMERIC].numeric;
  55. int nothing = 0;
  56. /* Now resolve copying and also handle completely missing definitions. */
  57. if (numeric == NULL)
  58. {
  59. /* First see whether we were supposed to copy. If yes, find the
  60. actual definition. */
  61. if (locale->copy_name[LC_NUMERIC] != NULL)
  62. {
  63. /* Find the copying locale. This has to happen transitively since
  64. the locale we are copying from might also copying another one. */
  65. struct localedef_t *from = locale;
  66. do
  67. from = find_locale (LC_NUMERIC, from->copy_name[LC_NUMERIC],
  68. from->repertoire_name, charmap);
  69. while (from->categories[LC_NUMERIC].numeric == NULL
  70. && from->copy_name[LC_NUMERIC] != NULL);
  71. numeric = locale->categories[LC_NUMERIC].numeric
  72. = from->categories[LC_NUMERIC].numeric;
  73. }
  74. /* If there is still no definition issue an warning and create an
  75. empty one. */
  76. if (numeric == NULL)
  77. {
  78. record_warning (_("\
  79. No definition for %s category found"), "LC_NUMERIC");
  80. numeric_startup (NULL, locale, 0);
  81. numeric = locale->categories[LC_NUMERIC].numeric;
  82. nothing = 1;
  83. }
  84. }
  85. /* The decimal point must not be empty. This is not said explicitly
  86. in POSIX but ANSI C (ISO/IEC 9899) says in 4.4.2.1 it has to be
  87. != "". */
  88. if (numeric->decimal_point == NULL)
  89. {
  90. if (! nothing)
  91. record_error (0, 0, _("%s: field `%s' not defined"),
  92. "LC_NUMERIC", "decimal_point");
  93. numeric->decimal_point = ".";
  94. }
  95. else if (numeric->decimal_point[0] == '\0' && ! nothing)
  96. {
  97. record_error (0, 0, _("\
  98. %s: value for field `%s' must not be an empty string"),
  99. "LC_NUMERIC", "decimal_point");
  100. }
  101. if (numeric->decimal_point_wc == L'\0')
  102. numeric->decimal_point_wc = L'.';
  103. if (numeric->grouping_len == 0 && ! nothing)
  104. record_error (0, 0, _("%s: field `%s' not defined"),
  105. "LC_NUMERIC", "grouping");
  106. }
  107. void
  108. numeric_output (struct localedef_t *locale, const struct charmap_t *charmap,
  109. const char *output_path)
  110. {
  111. struct locale_numeric_t *numeric = locale->categories[LC_NUMERIC].numeric;
  112. struct locale_file file;
  113. init_locale_data (&file, _NL_ITEM_INDEX (_NL_NUM_LC_NUMERIC));
  114. add_locale_string (&file, numeric->decimal_point ?: "");
  115. add_locale_string (&file, numeric->thousands_sep ?: "");
  116. add_locale_raw_data (&file, numeric->grouping, numeric->grouping_len);
  117. add_locale_uint32 (&file, numeric->decimal_point_wc);
  118. add_locale_uint32 (&file, numeric->thousands_sep_wc);
  119. add_locale_string (&file, charmap->code_set_name);
  120. write_locale_data (output_path, LC_NUMERIC, "LC_NUMERIC", &file);
  121. }
  122. /* The parser for the LC_NUMERIC section of the locale definition. */
  123. void
  124. numeric_read (struct linereader *ldfile, struct localedef_t *result,
  125. const struct charmap_t *charmap, const char *repertoire_name,
  126. int ignore_content)
  127. {
  128. struct repertoire_t *repertoire = NULL;
  129. struct locale_numeric_t *numeric;
  130. struct token *now;
  131. enum token_t nowtok;
  132. /* Get the repertoire we have to use. */
  133. if (repertoire_name != NULL)
  134. repertoire = repertoire_read (repertoire_name);
  135. /* The rest of the line containing `LC_NUMERIC' must be free. */
  136. lr_ignore_rest (ldfile, 1);
  137. do
  138. {
  139. now = lr_token (ldfile, charmap, result, NULL, verbose);
  140. nowtok = now->tok;
  141. }
  142. while (nowtok == tok_eol);
  143. /* If we see `copy' now we are almost done. */
  144. if (nowtok == tok_copy)
  145. {
  146. handle_copy (ldfile, charmap, repertoire_name, result, tok_lc_numeric,
  147. LC_NUMERIC, "LC_NUMERIC", ignore_content);
  148. return;
  149. }
  150. /* Prepare the data structures. */
  151. numeric_startup (ldfile, result, ignore_content);
  152. numeric = result->categories[LC_NUMERIC].numeric;
  153. while (1)
  154. {
  155. /* Of course we don't proceed beyond the end of file. */
  156. if (nowtok == tok_eof)
  157. break;
  158. /* Ignore empty lines. */
  159. if (nowtok == tok_eol)
  160. {
  161. now = lr_token (ldfile, charmap, result, NULL, verbose);
  162. nowtok = now->tok;
  163. continue;
  164. }
  165. switch (nowtok)
  166. {
  167. #define STR_ELEM(cat) \
  168. case tok_##cat: \
  169. /* Ignore the rest of the line if we don't need the input of \
  170. this line. */ \
  171. if (ignore_content) \
  172. { \
  173. lr_ignore_rest (ldfile, 0); \
  174. break; \
  175. } \
  176. \
  177. ldfile->return_widestr = 1; \
  178. now = lr_token (ldfile, charmap, result, repertoire, verbose); \
  179. if (now->tok != tok_string) \
  180. goto err_label; \
  181. if (numeric->cat != NULL) \
  182. lr_error (ldfile, _("\
  183. %s: field `%s' declared more than once"), "LC_NUMERIC", #cat); \
  184. else if (!ignore_content && now->val.str.startmb == NULL) \
  185. { \
  186. lr_error (ldfile, _("\
  187. %s: unknown character in field `%s'"), "LC_NUMERIC", #cat); \
  188. numeric->cat = ""; \
  189. numeric->cat##_wc = L'\0'; \
  190. } \
  191. else if (now->val.str.startwc != NULL && now->val.str.lenwc > 2) \
  192. { \
  193. lr_error (ldfile, _("\
  194. %s: value for field `%s' must be a single character"), "LC_NUMERIC", #cat); \
  195. } \
  196. else if (!ignore_content) \
  197. { \
  198. numeric->cat = now->val.str.startmb; \
  199. \
  200. if (now->val.str.startwc != NULL) \
  201. numeric->cat##_wc = *now->val.str.startwc; \
  202. } \
  203. ldfile->return_widestr = 0; \
  204. break
  205. STR_ELEM (decimal_point);
  206. STR_ELEM (thousands_sep);
  207. case tok_grouping:
  208. /* Ignore the rest of the line if we don't need the input of
  209. this line. */
  210. if (ignore_content)
  211. {
  212. lr_ignore_rest (ldfile, 0);
  213. break;
  214. }
  215. now = lr_token (ldfile, charmap, result, NULL, verbose);
  216. if (now->tok != tok_minus1 && now->tok != tok_number)
  217. goto err_label;
  218. else
  219. {
  220. size_t act = 0;
  221. size_t max = 10;
  222. char *grouping = xmalloc (max);
  223. do
  224. {
  225. if (act + 1 >= max)
  226. {
  227. max *= 2;
  228. grouping = xrealloc (grouping, max);
  229. }
  230. if (act > 0 && grouping[act - 1] == '\177')
  231. {
  232. lr_error (ldfile, _("\
  233. %s: `-1' must be last entry in `%s' field"), "LC_NUMERIC", "grouping");
  234. lr_ignore_rest (ldfile, 0);
  235. break;
  236. }
  237. if (now->tok == tok_minus1)
  238. grouping[act++] = '\177';
  239. else if (now->val.num == 0)
  240. {
  241. /* A value of 0 disables grouping from here on but
  242. we must not store a NUL character since this
  243. terminates the string. Use something different
  244. which must not be used otherwise. */
  245. grouping[act++] = '\377';
  246. }
  247. else if (now->val.num > 126)
  248. lr_error (ldfile, _("\
  249. %s: values for field `%s' must be smaller than 127"),
  250. "LC_NUMERIC", "grouping");
  251. else
  252. grouping[act++] = now->val.num;
  253. /* Next must be semicolon. */
  254. now = lr_token (ldfile, charmap, result, NULL, verbose);
  255. if (now->tok != tok_semicolon)
  256. break;
  257. now = lr_token (ldfile, charmap, result, NULL, verbose);
  258. }
  259. while (now->tok == tok_minus1 || now->tok == tok_number);
  260. if (now->tok != tok_eol)
  261. goto err_label;
  262. /* A single -1 means no grouping. */
  263. if (act == 1 && grouping[0] == '\177')
  264. act--;
  265. grouping[act++] = '\0';
  266. numeric->grouping = xrealloc (grouping, act);
  267. numeric->grouping_len = act;
  268. }
  269. break;
  270. case tok_end:
  271. /* Next we assume `LC_NUMERIC'. */
  272. now = lr_token (ldfile, charmap, result, NULL, verbose);
  273. if (now->tok == tok_eof)
  274. break;
  275. if (now->tok == tok_eol)
  276. lr_error (ldfile, _("%s: incomplete `END' line"), "LC_NUMERIC");
  277. else if (now->tok != tok_lc_numeric)
  278. lr_error (ldfile, _("\
  279. %1$s: definition does not end with `END %1$s'"), "LC_NUMERIC");
  280. lr_ignore_rest (ldfile, now->tok == tok_lc_numeric);
  281. return;
  282. default:
  283. err_label:
  284. SYNTAX_ERROR (_("%s: syntax error"), "LC_NUMERIC");
  285. }
  286. /* Prepare for the next round. */
  287. now = lr_token (ldfile, charmap, result, NULL, verbose);
  288. nowtok = now->tok;
  289. }
  290. /* When we come here we reached the end of the file. */
  291. lr_error (ldfile, _("%s: premature end of file"), "LC_NUMERIC");
  292. }