1 /*-
2 * Copyright (c) 2001 Alexey Zelkin <phantom@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 #define _GNU_SOURCE
28
29 #include <sys/cdefs.h>
30
31 #include <locale.h>
32 #include <langinfo.h>
33 #include <limits.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include "setlocale.h"
38
39 #undef offsetoff
40 #define _O(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER)
41
42 #define _NLITEM(cat,memb) { { .cat = __get_##cat##_locale }, \
43 _O (struct lc_##cat##_T, memb) }
44
45 #ifdef __HAVE_LOCALE_INFO_EXTENDED__
46 static struct _nl_item_t
47 {
48 union {
49 const struct lc_ctype_T * (*ctype)(struct __locale_t *);
50 const struct lc_time_T * (*time)(struct __locale_t *);
51 const struct lc_numeric_T * (*numeric)(struct __locale_t *);
52 const struct lc_monetary_T * (*monetary)(struct __locale_t *);
53 const struct lc_messages_T * (*messages)(struct __locale_t *);
54 void * (*base)(struct __locale_t *);
55 };
56 _off_t offset;
57 } nl_ext[] =
58 {
59 /* First element has an nl_item value of _NL_LOCALE_EXTENDED_FIRST_ENTRY */
60 _NLITEM (ctype, outdigits[0]),
61 _NLITEM (ctype, outdigits[1]),
62 _NLITEM (ctype, outdigits[2]),
63 _NLITEM (ctype, outdigits[3]),
64 _NLITEM (ctype, outdigits[4]),
65 _NLITEM (ctype, outdigits[5]),
66 _NLITEM (ctype, outdigits[6]),
67 _NLITEM (ctype, outdigits[7]),
68 _NLITEM (ctype, outdigits[8]),
69 _NLITEM (ctype, outdigits[9]),
70 _NLITEM (ctype, woutdigits[0]),
71 _NLITEM (ctype, woutdigits[1]),
72 _NLITEM (ctype, woutdigits[2]),
73 _NLITEM (ctype, woutdigits[3]),
74 _NLITEM (ctype, woutdigits[4]),
75 _NLITEM (ctype, woutdigits[5]),
76 _NLITEM (ctype, woutdigits[6]),
77 _NLITEM (ctype, woutdigits[7]),
78 _NLITEM (ctype, woutdigits[8]),
79 _NLITEM (ctype, woutdigits[9]),
80 _NLITEM (time, codeset),
81 _NLITEM (time, wmon[1]),
82 _NLITEM (time, wmon[2]),
83 _NLITEM (time, wmon[3]),
84 _NLITEM (time, wmon[4]),
85 _NLITEM (time, wmon[5]),
86 _NLITEM (time, wmon[6]),
87 _NLITEM (time, wmon[7]),
88 _NLITEM (time, wmon[8]),
89 _NLITEM (time, wmon[9]),
90 _NLITEM (time, wmon[10]),
91 _NLITEM (time, wmon[11]),
92 _NLITEM (time, wmon[12]),
93 _NLITEM (time, wmonth[1]),
94 _NLITEM (time, wmonth[2]),
95 _NLITEM (time, wmonth[3]),
96 _NLITEM (time, wmonth[4]),
97 _NLITEM (time, wmonth[5]),
98 _NLITEM (time, wmonth[6]),
99 _NLITEM (time, wmonth[7]),
100 _NLITEM (time, wmonth[8]),
101 _NLITEM (time, wmonth[9]),
102 _NLITEM (time, wmonth[10]),
103 _NLITEM (time, wmonth[11]),
104 _NLITEM (time, wmonth[12]),
105 _NLITEM (time, wwday[1]),
106 _NLITEM (time, wwday[2]),
107 _NLITEM (time, wwday[3]),
108 _NLITEM (time, wwday[4]),
109 _NLITEM (time, wwday[5]),
110 _NLITEM (time, wwday[6]),
111 _NLITEM (time, wwday[7]),
112 _NLITEM (time, wweekday[1]),
113 _NLITEM (time, wweekday[2]),
114 _NLITEM (time, wweekday[3]),
115 _NLITEM (time, wweekday[4]),
116 _NLITEM (time, wweekday[5]),
117 _NLITEM (time, wweekday[6]),
118 _NLITEM (time, wweekday[7]),
119 _NLITEM (time, wX_fmt),
120 _NLITEM (time, wx_fmt),
121 _NLITEM (time, wc_fmt),
122 _NLITEM (time, wam_pm[0]),
123 _NLITEM (time, wam_pm[1]),
124 _NLITEM (time, wdate_fmt),
125 _NLITEM (time, wampm_fmt),
126 _NLITEM (time, wera),
127 _NLITEM (time, wera_d_fmt),
128 _NLITEM (time, wera_d_t_fmt),
129 _NLITEM (time, wera_t_fmt),
130 _NLITEM (time, walt_digits),
131 _NLITEM (numeric, codeset),
132 _NLITEM (numeric, grouping),
133 _NLITEM (numeric, wdecimal_point),
134 _NLITEM (numeric, wthousands_sep),
135 _NLITEM (monetary, int_curr_symbol),
136 _NLITEM (monetary, currency_symbol),
137 _NLITEM (monetary, mon_decimal_point),
138 _NLITEM (monetary, mon_thousands_sep),
139 _NLITEM (monetary, mon_grouping),
140 _NLITEM (monetary, positive_sign),
141 _NLITEM (monetary, negative_sign),
142 _NLITEM (monetary, int_frac_digits),
143 _NLITEM (monetary, frac_digits),
144 _NLITEM (monetary, p_cs_precedes),
145 _NLITEM (monetary, p_sep_by_space),
146 _NLITEM (monetary, n_cs_precedes),
147 _NLITEM (monetary, n_sep_by_space),
148 _NLITEM (monetary, p_sign_posn),
149 _NLITEM (monetary, n_sign_posn),
150 _NLITEM (monetary, int_p_cs_precedes),
151 _NLITEM (monetary, int_p_sep_by_space),
152 _NLITEM (monetary, int_n_cs_precedes),
153 _NLITEM (monetary, int_n_sep_by_space),
154 _NLITEM (monetary, int_p_sign_posn),
155 _NLITEM (monetary, int_n_sign_posn),
156 _NLITEM (monetary, codeset),
157 _NLITEM (monetary, wint_curr_symbol),
158 _NLITEM (monetary, wcurrency_symbol),
159 _NLITEM (monetary, wmon_decimal_point),
160 _NLITEM (monetary, wmon_thousands_sep),
161 _NLITEM (monetary, wpositive_sign),
162 _NLITEM (monetary, wnegative_sign),
163 _NLITEM (messages, codeset),
164 _NLITEM (messages, wyesexpr),
165 _NLITEM (messages, wnoexpr),
166 _NLITEM (messages, wyesstr),
167 _NLITEM (messages, wnostr),
168 };
169 #endif /* __HAVE_LOCALE_INFO_EXTENDED__ */
170
171 #define _REL(BASE) ((int)item-BASE)
172
nl_langinfo_l(nl_item item,struct __locale_t * locale)173 char *nl_langinfo_l (nl_item item, struct __locale_t *locale)
174 {
175 char *ret, *cs;
176 static char *csym = NULL;
177 char *nptr;
178
179 switch (item) {
180 #ifdef __HAVE_LOCALE_INFO__
181 case _NL_MESSAGES_CODESET:
182 ret = (char *) __locale_msgcharset();
183 goto do_codeset;
184 #ifdef __HAVE_LOCALE_INFO_EXTENDED__
185 case _NL_TIME_CODESET:
186 ret = (char *) __get_time_locale (locale)->codeset;
187 goto do_codeset;
188 case _NL_NUMERIC_CODESET:
189 ret = (char *) __get_numeric_locale (locale)->codeset;
190 goto do_codeset;
191 case _NL_MONETARY_CODESET:
192 ret = (char *) __get_monetary_locale (locale)->codeset;
193 goto do_codeset;
194 #ifdef __CYGWIN__
195 case _NL_COLLATE_CODESET:
196 {
197 ret = (char *) __get_collate_locale (locale)->codeset;
198 goto do_codeset;
199 }
200 #endif /* __CYGWIN__ */
201 #endif /* __HAVE_LOCALE_INFO_EXTENDED__ */
202 #endif /* __HAVE_LOCALE_INFO__ */
203 case CODESET:
204 #ifdef _MB_CAPABLE
205 ret = (char *) __locale_charset (locale);
206 #endif
207 #ifdef __HAVE_LOCALE_INFO__
208 do_codeset:
209 #endif
210 #ifdef __CYGWIN__
211 /* Convert charset to Linux compatible codeset string. */
212 if (ret[0] == 'A'/*SCII*/)
213 ret = "ANSI_X3.4-1968";
214 else if (ret[0] == 'E')
215 {
216 if (strcmp (ret, "EUCJP") == 0)
217 ret = "EUC-JP";
218 else if (strcmp (ret, "EUCKR") == 0)
219 ret = "EUC-KR";
220 else if (strcmp (ret, "EUCCN") == 0)
221 ret = "GB2312";
222 }
223 else if (ret[0] == 'C'/*Pxxxx*/)
224 {
225 if (strcmp (ret + 2, "874") == 0)
226 ret = "TIS-620";
227 else if (strcmp (ret + 2, "20866") == 0)
228 ret = "KOI8-R";
229 else if (strcmp (ret + 2, "21866") == 0)
230 ret = "KOI8-U";
231 else if (strcmp (ret + 2, "101") == 0)
232 ret = "GEORGIAN-PS";
233 else if (strcmp (ret + 2, "102") == 0)
234 ret = "PT154";
235 else if (strcmp (ret + 2, "103") == 0)
236 ret = "KOI8-T";
237 }
238 else if (ret[0] == 'S'/*JIS*/)
239 {
240 /* Cygwin uses MSFT's implementation of SJIS, which differs
241 in some codepoints from the real thing, especially
242 0x5c: yen sign instead of backslash,
243 0x7e: overline instead of tilde.
244 We can't use the real SJIS since otherwise Win32
245 pathnames would become invalid. OTOH, if we return
246 "SJIS" here, then libiconv will do mb<->wc conversion
247 differently to our internal functions. Therefore we
248 return what we really implement, CP932. This is handled
249 fine by libiconv. */
250 ret = "CP932";
251 }
252 #elif !defined (_MB_CAPABLE)
253 ret = "US-ASCII";
254 #endif /* __CYGWIN__ */
255 break;
256 case D_T_FMT:
257 ret = (char *) __get_time_locale (locale)->c_fmt;
258 break;
259 case D_FMT:
260 ret = (char *) __get_time_locale (locale)->x_fmt;
261 break;
262 case T_FMT:
263 ret = (char *) __get_time_locale (locale)->X_fmt;
264 break;
265 case T_FMT_AMPM:
266 ret = (char *) __get_time_locale (locale)->ampm_fmt;
267 break;
268 case AM_STR:
269 ret = (char *) __get_time_locale (locale)->am_pm[0];
270 break;
271 case PM_STR:
272 ret = (char *) __get_time_locale (locale)->am_pm[1];
273 break;
274 case DAY_1: case DAY_2: case DAY_3:
275 case DAY_4: case DAY_5: case DAY_6: case DAY_7:
276 ret = (char*) __get_time_locale (locale)->weekday[_REL(DAY_1)];
277 break;
278 case ABDAY_1: case ABDAY_2: case ABDAY_3:
279 case ABDAY_4: case ABDAY_5: case ABDAY_6: case ABDAY_7:
280 ret = (char*) __get_time_locale (locale)->wday[_REL(ABDAY_1)];
281 break;
282 case MON_1: case MON_2: case MON_3: case MON_4:
283 case MON_5: case MON_6: case MON_7: case MON_8:
284 case MON_9: case MON_10: case MON_11: case MON_12:
285 ret = (char*) __get_time_locale (locale)->month[_REL(MON_1)];
286 break;
287 case ABMON_1: case ABMON_2: case ABMON_3: case ABMON_4:
288 case ABMON_5: case ABMON_6: case ABMON_7: case ABMON_8:
289 case ABMON_9: case ABMON_10: case ABMON_11: case ABMON_12:
290 ret = (char*) __get_time_locale (locale)->mon[_REL(ABMON_1)];
291 break;
292 case ERA:
293 ret = (char*) __get_time_locale (locale)->era;
294 break;
295 case ERA_D_FMT:
296 ret = (char*) __get_time_locale (locale)->era_d_fmt;
297 break;
298 case ERA_D_T_FMT:
299 ret = (char*) __get_time_locale (locale)->era_d_t_fmt;
300 break;
301 case ERA_T_FMT:
302 ret = (char*) __get_time_locale (locale)->era_t_fmt;
303 break;
304 case ALT_DIGITS:
305 ret = (char*) __get_time_locale (locale)->alt_digits;
306 break;
307 case _DATE_FMT: /* GNU extension */
308 ret = (char*) __get_time_locale (locale)->date_fmt;
309 break;
310 case RADIXCHAR:
311 ret = (char*) __get_numeric_locale (locale)->decimal_point;
312 break;
313 case THOUSEP:
314 ret = (char*) __get_numeric_locale (locale)->thousands_sep;
315 break;
316 case YESEXPR:
317 ret = (char*) __get_messages_locale (locale)->yesexpr;
318 break;
319 case NOEXPR:
320 ret = (char*) __get_messages_locale (locale)->noexpr;
321 break;
322 /*
323 * All items marked with LEGACY are available, but not recomended
324 * by SUSv2 to be used in portable applications since they're subject
325 * to remove in future specification editions
326 */
327 case YESSTR: /* LEGACY */
328 ret = (char*) __get_messages_locale (locale)->yesstr;
329 break;
330 case NOSTR: /* LEGACY */
331 ret = (char*) __get_messages_locale (locale)->nostr;
332 break;
333 case CRNCYSTR:
334 ret = "";
335 cs = (char*) __get_monetary_locale (locale)->currency_symbol;
336 if (*cs != '\0') {
337 char pos = __localeconv_l (locale)->p_cs_precedes;
338
339 if (pos == __localeconv_l (locale)->n_cs_precedes) {
340 char psn = '\0';
341
342 if (pos == CHAR_MAX) {
343 if (strcmp(cs, __get_monetary_locale (locale)->mon_decimal_point) == 0)
344 psn = '.';
345 } else
346 psn = pos ? '-' : '+';
347 if (psn != '\0') {
348 int clen = strlen(cs);
349
350 nptr = realloc(csym, clen + 2);
351 if (!nptr && csym)
352 free (csym);
353
354 csym = nptr;
355
356 if (csym != NULL) {
357 *csym = psn;
358 strcpy(csym + 1, cs);
359 ret = csym;
360 }
361 }
362 }
363 }
364 break;
365 case D_MD_ORDER: /* local extension */
366 ret = (char *) __get_time_locale (locale)->md_order;
367 break;
368 #ifdef __HAVE_LOCALE_INFO__
369 case _NL_CTYPE_MB_CUR_MAX:
370 ret = (char *) __get_ctype_locale (locale)->mb_cur_max;
371 break;
372 #endif
373 default:
374 /* Relies on the fact that LC_ALL is 0, and all other
375 LC_ constants are in ascending order. */
376 if (item > NL_LOCALE_NAME(LC_ALL)
377 && item < NL_LOCALE_NAME(_LC_LAST)) {
378 return locale->categories[item
379 - NL_LOCALE_NAME(LC_ALL)];
380 }
381 #ifdef __HAVE_LOCALE_INFO_EXTENDED__
382 if (item > _NL_LOCALE_EXTENDED_FIRST_ENTRY
383 && item < _NL_LOCALE_EXTENDED_LAST_ENTRY) {
384 int idx = item - _NL_LOCALE_EXTENDED_FIRST_ENTRY - 1;
385 return *(char **) ((char *) (*nl_ext[idx].base)(locale)
386 + nl_ext[idx].offset);
387 }
388 #endif
389 ret = "";
390 }
391 return (ret);
392 }
393
nl_langinfo(nl_item item)394 char *nl_langinfo (nl_item item)
395 {
396 return nl_langinfo_l (item, __get_current_locale ());
397 }
398