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 #ifndef __CYGWIN__
177 char *s;
178 #endif
179 static char *csym = NULL;
180 char *nptr;
181
182 switch (item) {
183 #ifdef __HAVE_LOCALE_INFO__
184 case _NL_MESSAGES_CODESET:
185 ret = (char *) __get_messages_locale (locale)->codeset;
186 goto do_codeset;
187 #ifdef __HAVE_LOCALE_INFO_EXTENDED__
188 case _NL_TIME_CODESET:
189 ret = (char *) __get_time_locale (locale)->codeset;
190 goto do_codeset;
191 case _NL_NUMERIC_CODESET:
192 ret = (char *) __get_numeric_locale (locale)->codeset;
193 goto do_codeset;
194 case _NL_MONETARY_CODESET:
195 ret = (char *) __get_monetary_locale (locale)->codeset;
196 goto do_codeset;
197 #ifdef __CYGWIN__
198 case _NL_COLLATE_CODESET:
199 {
200 ret = (char *) __get_collate_locale (locale)->codeset;
201 goto do_codeset;
202 }
203 #endif /* __CYGWIN__ */
204 #endif /* __HAVE_LOCALE_INFO_EXTENDED__ */
205 #endif /* __HAVE_LOCALE_INFO__ */
206 case CODESET:
207 #ifdef _MB_CAPABLE
208 ret = (char *) __locale_charset (locale);
209 #endif
210 do_codeset:
211 #ifdef __CYGWIN__
212 /* Convert charset to Linux compatible codeset string. */
213 if (ret[0] == 'A'/*SCII*/)
214 ret = "ANSI_X3.4-1968";
215 else if (ret[0] == 'E')
216 {
217 if (strcmp (ret, "EUCJP") == 0)
218 ret = "EUC-JP";
219 else if (strcmp (ret, "EUCKR") == 0)
220 ret = "EUC-KR";
221 else if (strcmp (ret, "EUCCN") == 0)
222 ret = "GB2312";
223 }
224 else if (ret[0] == 'C'/*Pxxxx*/)
225 {
226 if (strcmp (ret + 2, "874") == 0)
227 ret = "TIS-620";
228 else if (strcmp (ret + 2, "20866") == 0)
229 ret = "KOI8-R";
230 else if (strcmp (ret + 2, "21866") == 0)
231 ret = "KOI8-U";
232 else if (strcmp (ret + 2, "101") == 0)
233 ret = "GEORGIAN-PS";
234 else if (strcmp (ret + 2, "102") == 0)
235 ret = "PT154";
236 }
237 else if (ret[0] == 'S'/*JIS*/)
238 {
239 /* Cygwin uses MSFT's implementation of SJIS, which differs
240 in some codepoints from the real thing, especially
241 0x5c: yen sign instead of backslash,
242 0x7e: overline instead of tilde.
243 We can't use the real SJIS since otherwise Win32
244 pathnames would become invalid. OTOH, if we return
245 "SJIS" here, then libiconv will do mb<->wc conversion
246 differently to our internal functions. Therefore we
247 return what we really implement, CP932. This is handled
248 fine by libiconv. */
249 ret = "CP932";
250 }
251 #elif !defined (_MB_CAPABLE)
252 ret = "US-ASCII";
253 #endif /* __CYGWIN__ */
254 break;
255 case D_T_FMT:
256 ret = (char *) __get_time_locale (locale)->c_fmt;
257 break;
258 case D_FMT:
259 ret = (char *) __get_time_locale (locale)->x_fmt;
260 break;
261 case T_FMT:
262 ret = (char *) __get_time_locale (locale)->X_fmt;
263 break;
264 case T_FMT_AMPM:
265 ret = (char *) __get_time_locale (locale)->ampm_fmt;
266 break;
267 case AM_STR:
268 ret = (char *) __get_time_locale (locale)->am_pm[0];
269 break;
270 case PM_STR:
271 ret = (char *) __get_time_locale (locale)->am_pm[1];
272 break;
273 case DAY_1: case DAY_2: case DAY_3:
274 case DAY_4: case DAY_5: case DAY_6: case DAY_7:
275 ret = (char*) __get_time_locale (locale)->weekday[_REL(DAY_1)];
276 break;
277 case ABDAY_1: case ABDAY_2: case ABDAY_3:
278 case ABDAY_4: case ABDAY_5: case ABDAY_6: case ABDAY_7:
279 ret = (char*) __get_time_locale (locale)->wday[_REL(ABDAY_1)];
280 break;
281 case MON_1: case MON_2: case MON_3: case MON_4:
282 case MON_5: case MON_6: case MON_7: case MON_8:
283 case MON_9: case MON_10: case MON_11: case MON_12:
284 ret = (char*) __get_time_locale (locale)->month[_REL(MON_1)];
285 break;
286 case ABMON_1: case ABMON_2: case ABMON_3: case ABMON_4:
287 case ABMON_5: case ABMON_6: case ABMON_7: case ABMON_8:
288 case ABMON_9: case ABMON_10: case ABMON_11: case ABMON_12:
289 ret = (char*) __get_time_locale (locale)->mon[_REL(ABMON_1)];
290 break;
291 case ERA:
292 ret = (char*) __get_time_locale (locale)->era;
293 break;
294 case ERA_D_FMT:
295 ret = (char*) __get_time_locale (locale)->era_d_fmt;
296 break;
297 case ERA_D_T_FMT:
298 ret = (char*) __get_time_locale (locale)->era_d_t_fmt;
299 break;
300 case ERA_T_FMT:
301 ret = (char*) __get_time_locale (locale)->era_t_fmt;
302 break;
303 case ALT_DIGITS:
304 ret = (char*) __get_time_locale (locale)->alt_digits;
305 break;
306 case _DATE_FMT: /* GNU extension */
307 ret = (char*) __get_time_locale (locale)->date_fmt;
308 break;
309 case RADIXCHAR:
310 ret = (char*) __get_numeric_locale (locale)->decimal_point;
311 break;
312 case THOUSEP:
313 ret = (char*) __get_numeric_locale (locale)->thousands_sep;
314 break;
315 case YESEXPR:
316 ret = (char*) __get_messages_locale (locale)->yesexpr;
317 break;
318 case NOEXPR:
319 ret = (char*) __get_messages_locale (locale)->noexpr;
320 break;
321 /*
322 * All items marked with LEGACY are available, but not recomended
323 * by SUSv2 to be used in portable applications since they're subject
324 * to remove in future specification editions
325 */
326 case YESSTR: /* LEGACY */
327 ret = (char*) __get_messages_locale (locale)->yesstr;
328 break;
329 case NOSTR: /* LEGACY */
330 ret = (char*) __get_messages_locale (locale)->nostr;
331 break;
332 case CRNCYSTR:
333 ret = "";
334 cs = (char*) __get_monetary_locale (locale)->currency_symbol;
335 if (*cs != '\0') {
336 char pos = __localeconv_l (locale)->p_cs_precedes;
337
338 if (pos == __localeconv_l (locale)->n_cs_precedes) {
339 char psn = '\0';
340
341 if (pos == CHAR_MAX) {
342 if (strcmp(cs, __get_monetary_locale (locale)->mon_decimal_point) == 0)
343 psn = '.';
344 } else
345 psn = pos ? '-' : '+';
346 if (psn != '\0') {
347 int clen = strlen(cs);
348
349 nptr = realloc(csym, clen + 2);
350 if (!nptr && csym)
351 free (csym);
352
353 csym = nptr;
354
355 if (csym != NULL) {
356 *csym = psn;
357 strcpy(csym + 1, cs);
358 ret = csym;
359 }
360 }
361 }
362 }
363 break;
364 case D_MD_ORDER: /* local extension */
365 ret = (char *) __get_time_locale (locale)->md_order;
366 break;
367 #ifdef __HAVE_LOCALE_INFO__
368 case _NL_CTYPE_MB_CUR_MAX:
369 ret = (char *) __get_ctype_locale (locale)->mb_cur_max;
370 break;
371 #endif
372 default:
373 /* Relies on the fact that LC_ALL is 0, and all other
374 LC_ constants are in ascending order. */
375 if (item > NL_LOCALE_NAME(LC_ALL)
376 && item < NL_LOCALE_NAME(_LC_LAST)) {
377 return locale->categories[item
378 - NL_LOCALE_NAME(LC_ALL)];
379 }
380 #ifdef __HAVE_LOCALE_INFO_EXTENDED__
381 if (item > _NL_LOCALE_EXTENDED_FIRST_ENTRY
382 && item < _NL_LOCALE_EXTENDED_LAST_ENTRY) {
383 int idx = item - _NL_LOCALE_EXTENDED_FIRST_ENTRY - 1;
384 return *(char **) ((char *) (*nl_ext[idx].base)(locale)
385 + nl_ext[idx].offset);
386 }
387 #endif
388 ret = "";
389 }
390 return (ret);
391 }
392
nl_langinfo(nl_item item)393 char *nl_langinfo (nl_item item)
394 {
395 return nl_langinfo_l (item, __get_current_locale ());
396 }
397