1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <locale.h>
17 #include <limits.h>
18 #include "libc.h"
19 #include <string.h>
20 #include "locale_impl.h"
21
22 static const struct lconv posix_lconv = {
23 .decimal_point = ".",
24 .thousands_sep = "",
25 .grouping = "",
26 .int_curr_symbol = "",
27 .currency_symbol = "",
28 .mon_decimal_point = "",
29 .mon_thousands_sep = "",
30 .mon_grouping = "",
31 .positive_sign = "",
32 .negative_sign = "",
33 .int_frac_digits = CHAR_MAX,
34 .frac_digits = CHAR_MAX,
35 .p_cs_precedes = CHAR_MAX,
36 .p_sep_by_space = CHAR_MAX,
37 .n_cs_precedes = CHAR_MAX,
38 .n_sep_by_space = CHAR_MAX,
39 .p_sign_posn = CHAR_MAX,
40 .n_sign_posn = CHAR_MAX,
41 .int_p_cs_precedes = CHAR_MAX,
42 .int_p_sep_by_space = CHAR_MAX,
43 .int_n_cs_precedes = CHAR_MAX,
44 .int_n_sep_by_space = CHAR_MAX,
45 .int_p_sign_posn = CHAR_MAX,
46 .int_n_sign_posn = CHAR_MAX,
47 };
48
49 #ifdef FEATURE_ICU_LOCALE
50 #define ICU_BUFFER_SIZE 16
51 #define CHAR_BUFFER_SIZE 8
52 #define INITIALIZE_ICURES_PTR(posix_lconv_ptr, lconv_icures_ptr, buffsize) \
53 do { \
54 memset(lconv_icures_ptr, 0, buffsize); \
55 strncpy(lconv_icures_ptr, posix_lconv_ptr, strlen(posix_lconv_ptr)); \
56 } while (0)
57
58 static struct lconv g_lconv_icures;
59 static int g_localeconv_initialize = 1;
60
61 typedef enum {
62 ICU_DECIMAL_POINT = 0,
63 ICU_THOUSANDS_SEP = 1,
64 ICU_NEGATIVE_SIGN = 6,
65 ICU_POSITIVE_SIGN = 7,
66 ICU_CURR_SYMBOL = 8,
67 ICU_INT_CURR_SYMBOL = 9,
68 ICU_MON_DECIMAL_POINT = 10,
69 ICU_MON_THOUSANDS_SEP = 17,
70 } icu_getsymbol_type;
71
update_lconv_member(u_char * icu_symbol,void * fmt,char * lconv_member,icu_getsymbol_type type)72 static void update_lconv_member(u_char *icu_symbol, void *fmt, char *lconv_member, icu_getsymbol_type type)
73 {
74 int icu_status = 0;
75 if (!g_icu_opt_func.unum_get_symbol) {
76 return;
77 }
78 int len = g_icu_opt_func.unum_get_symbol(fmt, type, icu_symbol, ICU_BUFFER_SIZE, &icu_status);
79 if (icu_status <= 0) {
80 if (g_icu_opt_func.u_austrncpy) {
81 g_icu_opt_func.u_austrncpy(lconv_member, icu_symbol, ICU_BUFFER_SIZE);
82 }
83 }
84 }
85
86 // refresh the lconv_icures, fills with the default value
refresh_lconv_icures(void)87 static void refresh_lconv_icures(void)
88 {
89 if (g_localeconv_initialize == 1) {
90 memcpy(&g_lconv_icures, &posix_lconv, sizeof(struct lconv));
91 g_lconv_icures.decimal_point = (char *)malloc(ICU_BUFFER_SIZE);
92 g_lconv_icures.thousands_sep = (char *)malloc(ICU_BUFFER_SIZE);
93 g_lconv_icures.int_curr_symbol = (char *)malloc(ICU_BUFFER_SIZE);
94 g_lconv_icures.currency_symbol = (char *)malloc(ICU_BUFFER_SIZE);
95 g_lconv_icures.mon_decimal_point = (char *)malloc(ICU_BUFFER_SIZE);
96 g_lconv_icures.mon_thousands_sep = (char *)malloc(ICU_BUFFER_SIZE);
97 g_lconv_icures.positive_sign = (char *)malloc(ICU_BUFFER_SIZE);
98 g_lconv_icures.negative_sign = (char *)malloc(ICU_BUFFER_SIZE);
99 g_localeconv_initialize = 0;
100 }
101
102 INITIALIZE_ICURES_PTR(posix_lconv.decimal_point, g_lconv_icures.decimal_point, ICU_BUFFER_SIZE);
103 INITIALIZE_ICURES_PTR(posix_lconv.thousands_sep, g_lconv_icures.thousands_sep, ICU_BUFFER_SIZE);
104 INITIALIZE_ICURES_PTR(posix_lconv.int_curr_symbol, g_lconv_icures.int_curr_symbol, ICU_BUFFER_SIZE);
105 INITIALIZE_ICURES_PTR(posix_lconv.currency_symbol, g_lconv_icures.currency_symbol, ICU_BUFFER_SIZE);
106 INITIALIZE_ICURES_PTR(posix_lconv.mon_decimal_point, g_lconv_icures.mon_decimal_point, ICU_BUFFER_SIZE);
107 INITIALIZE_ICURES_PTR(posix_lconv.mon_thousands_sep, g_lconv_icures.mon_thousands_sep, ICU_BUFFER_SIZE);
108 INITIALIZE_ICURES_PTR(posix_lconv.positive_sign, g_lconv_icures.positive_sign, ICU_BUFFER_SIZE);
109 INITIALIZE_ICURES_PTR(posix_lconv.negative_sign, g_lconv_icures.negative_sign, ICU_BUFFER_SIZE);
110 }
111 #endif
112
localeconv(void)113 struct lconv *localeconv(void)
114 {
115 #ifdef FEATURE_ICU_LOCALE
116 if ((libc.global_locale.cat[LC_NUMERIC] && libc.global_locale.cat[LC_NUMERIC]->flag == ICU_VALID) ||
117 (libc.global_locale.cat[LC_MONETARY] && libc.global_locale.cat[LC_MONETARY]->flag == ICU_VALID)) {
118 int cur_status = 0;
119 refresh_lconv_icures();
120
121 /* ICU function: unum_getSymbol, return specific information, e.g. currency symbol */
122 get_icu_symbol(ICU_I18N, &(g_icu_opt_func.unum_get_symbol), ICU_UNUM_GET_SYMBOL_SYMBOL);
123 /* ICU function: u_austrncpy, transfer result from unum_getSymbol to utf-8 string */
124 get_icu_symbol(ICU_UC, &(g_icu_opt_func.u_austrncpy), ICU_AUSTRNCPY_SYMBOL);
125
126 if (!(g_icu_opt_func.unum_get_symbol) || !(g_icu_opt_func.u_austrncpy)) {
127 return (void *)&g_lconv_icures;
128 }
129
130 u_char icu_symbol[ICU_BUFFER_SIZE] = { 0 };
131 void *fmt = NULL;
132
133 if (libc.global_locale.cat[LC_NUMERIC]) {
134 char *icu_locale_name_num = calloc(1, MAX_VALID_ICU_NAME_LEN);
135 get_valid_icu_locale_name(libc.global_locale.cat[LC_NUMERIC]->name, icu_locale_name_num,
136 MAX_VALID_ICU_NAME_LEN);
137 fmt = icu_unum_open(icu_locale_name_num, &cur_status);
138 free(icu_locale_name_num);
139 if (cur_status == 0) {
140 memset(icu_symbol, 0, ICU_BUFFER_SIZE);
141 update_lconv_member(icu_symbol, fmt, g_lconv_icures.decimal_point, ICU_DECIMAL_POINT);
142 memset(icu_symbol, 0, ICU_BUFFER_SIZE);
143 update_lconv_member(icu_symbol, fmt, g_lconv_icures.thousands_sep, ICU_THOUSANDS_SEP);
144 icu_unum_close(fmt);
145 }
146 }
147
148 if (libc.global_locale.cat[LC_MONETARY]) {
149 char *icu_locale_name_mon = calloc(1, MAX_VALID_ICU_NAME_LEN);
150 get_valid_icu_locale_name(libc.global_locale.cat[LC_MONETARY]->name, icu_locale_name_mon,
151 MAX_VALID_ICU_NAME_LEN);
152 fmt = icu_unum_open(icu_locale_name_mon, &cur_status);
153 free(icu_locale_name_mon);
154 if (cur_status == 0) {
155 memset(icu_symbol, 0, ICU_BUFFER_SIZE);
156 update_lconv_member(icu_symbol, fmt, g_lconv_icures.int_curr_symbol, ICU_INT_CURR_SYMBOL);
157 memset(icu_symbol, 0, ICU_BUFFER_SIZE);
158 update_lconv_member(icu_symbol, fmt, g_lconv_icures.currency_symbol, ICU_CURR_SYMBOL);
159 memset(icu_symbol, 0, ICU_BUFFER_SIZE);
160 update_lconv_member(icu_symbol, fmt, g_lconv_icures.mon_decimal_point, ICU_MON_DECIMAL_POINT);
161 memset(icu_symbol, 0, ICU_BUFFER_SIZE);
162 update_lconv_member(icu_symbol, fmt, g_lconv_icures.mon_thousands_sep, ICU_MON_THOUSANDS_SEP);
163 memset(icu_symbol, 0, ICU_BUFFER_SIZE);
164 update_lconv_member(icu_symbol, fmt, g_lconv_icures.positive_sign, ICU_POSITIVE_SIGN);
165 memset(icu_symbol, 0, ICU_BUFFER_SIZE);
166 update_lconv_member(icu_symbol, fmt, g_lconv_icures.negative_sign, ICU_NEGATIVE_SIGN);
167 icu_unum_close(fmt);
168 }
169 }
170 return (void *)&g_lconv_icures;
171 }
172 #endif
173 return (void *)&posix_lconv;
174 }
175