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 #include "setlocale.h"
29 
30 #undef offsetoff
31 #define _O(TYPE, MEMBER)  __builtin_offsetof (TYPE, MEMBER)
32 
33 #define _NLITEM(cat,memb) { { .cat = __get_##cat##_locale }, \
34 			      _O (struct lc_##cat##_T, memb) }
35 
36 #ifdef __HAVE_LOCALE_INFO_EXTENDED__
37 static struct _nl_item_t
38 {
39   union {
40     const struct lc_ctype_T *    (*ctype)(struct __locale_t *);
41     const struct lc_time_T *     (*time)(struct __locale_t *);
42     const struct lc_numeric_T *  (*numeric)(struct __locale_t *);
43     const struct lc_monetary_T * (*monetary)(struct __locale_t *);
44     const struct lc_messages_T * (*messages)(struct __locale_t *);
45     void *			 (*base)(struct __locale_t *);
46   };
47   _off_t offset;
48 } nl_ext[] =
49 {
50   /* First element has an nl_item value of _NL_LOCALE_EXTENDED_FIRST_ENTRY */
51   _NLITEM (ctype, outdigits[0]),
52   _NLITEM (ctype, outdigits[1]),
53   _NLITEM (ctype, outdigits[2]),
54   _NLITEM (ctype, outdigits[3]),
55   _NLITEM (ctype, outdigits[4]),
56   _NLITEM (ctype, outdigits[5]),
57   _NLITEM (ctype, outdigits[6]),
58   _NLITEM (ctype, outdigits[7]),
59   _NLITEM (ctype, outdigits[8]),
60   _NLITEM (ctype, outdigits[9]),
61   _NLITEM (ctype, woutdigits[0]),
62   _NLITEM (ctype, woutdigits[1]),
63   _NLITEM (ctype, woutdigits[2]),
64   _NLITEM (ctype, woutdigits[3]),
65   _NLITEM (ctype, woutdigits[4]),
66   _NLITEM (ctype, woutdigits[5]),
67   _NLITEM (ctype, woutdigits[6]),
68   _NLITEM (ctype, woutdigits[7]),
69   _NLITEM (ctype, woutdigits[8]),
70   _NLITEM (ctype, woutdigits[9]),
71   _NLITEM (time, codeset),
72   _NLITEM (time, wmon[0]),
73   _NLITEM (time, wmon[1]),
74   _NLITEM (time, wmon[2]),
75   _NLITEM (time, wmon[3]),
76   _NLITEM (time, wmon[4]),
77   _NLITEM (time, wmon[5]),
78   _NLITEM (time, wmon[6]),
79   _NLITEM (time, wmon[7]),
80   _NLITEM (time, wmon[8]),
81   _NLITEM (time, wmon[9]),
82   _NLITEM (time, wmon[10]),
83   _NLITEM (time, wmon[11]),
84   _NLITEM (time, wmonth[0]),
85   _NLITEM (time, wmonth[1]),
86   _NLITEM (time, wmonth[2]),
87   _NLITEM (time, wmonth[3]),
88   _NLITEM (time, wmonth[4]),
89   _NLITEM (time, wmonth[5]),
90   _NLITEM (time, wmonth[6]),
91   _NLITEM (time, wmonth[7]),
92   _NLITEM (time, wmonth[8]),
93   _NLITEM (time, wmonth[9]),
94   _NLITEM (time, wmonth[10]),
95   _NLITEM (time, wmonth[11]),
96   _NLITEM (time, wwday[0]),
97   _NLITEM (time, wwday[1]),
98   _NLITEM (time, wwday[2]),
99   _NLITEM (time, wwday[3]),
100   _NLITEM (time, wwday[4]),
101   _NLITEM (time, wwday[5]),
102   _NLITEM (time, wwday[6]),
103   _NLITEM (time, wweekday[0]),
104   _NLITEM (time, wweekday[1]),
105   _NLITEM (time, wweekday[2]),
106   _NLITEM (time, wweekday[3]),
107   _NLITEM (time, wweekday[4]),
108   _NLITEM (time, wweekday[5]),
109   _NLITEM (time, wweekday[6]),
110   _NLITEM (time, wX_fmt),
111   _NLITEM (time, wx_fmt),
112   _NLITEM (time, wc_fmt),
113   _NLITEM (time, wam_pm[0]),
114   _NLITEM (time, wam_pm[1]),
115   _NLITEM (time, wdate_fmt),
116   _NLITEM (time, wampm_fmt),
117   _NLITEM (time, wera),
118   _NLITEM (time, wera_d_fmt),
119   _NLITEM (time, wera_d_t_fmt),
120   _NLITEM (time, wera_t_fmt),
121   _NLITEM (time, walt_digits),
122   _NLITEM (numeric, codeset),
123   _NLITEM (numeric, grouping),
124   _NLITEM (numeric, wdecimal_point),
125   _NLITEM (numeric, wthousands_sep),
126   _NLITEM (monetary, int_curr_symbol),
127   _NLITEM (monetary, currency_symbol),
128   _NLITEM (monetary, mon_decimal_point),
129   _NLITEM (monetary, mon_thousands_sep),
130   _NLITEM (monetary, mon_grouping),
131   _NLITEM (monetary, positive_sign),
132   _NLITEM (monetary, negative_sign),
133   _NLITEM (monetary, int_frac_digits),
134   _NLITEM (monetary, frac_digits),
135   _NLITEM (monetary, p_cs_precedes),
136   _NLITEM (monetary, p_sep_by_space),
137   _NLITEM (monetary, n_cs_precedes),
138   _NLITEM (monetary, n_sep_by_space),
139   _NLITEM (monetary, p_sign_posn),
140   _NLITEM (monetary, n_sign_posn),
141   _NLITEM (monetary, int_p_cs_precedes),
142   _NLITEM (monetary, int_p_sep_by_space),
143   _NLITEM (monetary, int_n_cs_precedes),
144   _NLITEM (monetary, int_n_sep_by_space),
145   _NLITEM (monetary, int_p_sign_posn),
146   _NLITEM (monetary, int_n_sign_posn),
147   _NLITEM (monetary, codeset),
148   _NLITEM (monetary, wint_curr_symbol),
149   _NLITEM (monetary, wcurrency_symbol),
150   _NLITEM (monetary, wmon_decimal_point),
151   _NLITEM (monetary, wmon_thousands_sep),
152   _NLITEM (monetary, wpositive_sign),
153   _NLITEM (monetary, wnegative_sign),
154   _NLITEM (messages, wyesexpr),
155   _NLITEM (messages, wnoexpr),
156   _NLITEM (messages, wyesstr),
157   _NLITEM (messages, wnostr),
158 };
159 #endif /* __HAVE_LOCALE_INFO_EXTENDED__ */
160 
161 #define _REL(BASE) ((int)item-BASE)
162 
nl_langinfo_l(nl_item item,struct __locale_t * locale)163 char *nl_langinfo_l (nl_item item, struct __locale_t *locale)
164 {
165    char *ret, *cs;
166    static char *csym = NULL;
167    char *nptr;
168 
169    switch (item) {
170 #ifdef __HAVE_LOCALE_INFO__
171 	case _NL_MESSAGES_CODESET:
172 		ret = (char *) __locale_msgcharset();
173 		goto do_codeset;
174 #ifdef __HAVE_LOCALE_INFO_EXTENDED__
175 	case _NL_TIME_CODESET:
176 		ret = (char *) __get_time_locale (locale)->codeset;
177 		goto do_codeset;
178 	case _NL_NUMERIC_CODESET:
179 		ret = (char *) __get_numeric_locale (locale)->codeset;
180 		goto do_codeset;
181 	case _NL_MONETARY_CODESET:
182 		ret = (char *) __get_monetary_locale (locale)->codeset;
183 		goto do_codeset;
184 #endif /* __HAVE_LOCALE_INFO_EXTENDED__ */
185 #endif /* __HAVE_LOCALE_INFO__ */
186 	case CODESET:
187 #ifdef _MB_CAPABLE
188 		ret = (char *) __locale_charset (locale);
189 #endif
190 #ifdef __HAVE_LOCALE_INFO__
191 do_codeset:
192 #endif
193 #if   !defined (_MB_CAPABLE)
194 		ret = "US-ASCII";
195 #endif /* __CYGWIN__ */
196 		break;
197 	case D_T_FMT:
198 		ret = (char *) __get_time_locale (locale)->c_fmt;
199 		break;
200 	case D_FMT:
201 		ret = (char *) __get_time_locale (locale)->x_fmt;
202 		break;
203 	case T_FMT:
204 		ret = (char *) __get_time_locale (locale)->X_fmt;
205 		break;
206 	case T_FMT_AMPM:
207 		ret = (char *) __get_time_locale (locale)->ampm_fmt;
208 		break;
209 	case AM_STR:
210 		ret = (char *) __get_time_locale (locale)->am_pm[0];
211 		break;
212 	case PM_STR:
213 		ret = (char *) __get_time_locale (locale)->am_pm[1];
214 		break;
215 	case DAY_1: case DAY_2: case DAY_3:
216 	case DAY_4: case DAY_5: case DAY_6: case DAY_7:
217 		ret = (char*) __get_time_locale (locale)->weekday[_REL(DAY_1)];
218 		break;
219 	case ABDAY_1: case ABDAY_2: case ABDAY_3:
220 	case ABDAY_4: case ABDAY_5: case ABDAY_6: case ABDAY_7:
221 		ret = (char*) __get_time_locale (locale)->wday[_REL(ABDAY_1)];
222 		break;
223 	case MON_1: case MON_2: case MON_3: case MON_4:
224 	case MON_5: case MON_6: case MON_7: case MON_8:
225 	case MON_9: case MON_10: case MON_11: case MON_12:
226 		ret = (char*) __get_time_locale (locale)->month[_REL(MON_1)];
227 		break;
228 	case ABMON_1: case ABMON_2: case ABMON_3: case ABMON_4:
229 	case ABMON_5: case ABMON_6: case ABMON_7: case ABMON_8:
230 	case ABMON_9: case ABMON_10: case ABMON_11: case ABMON_12:
231 		ret = (char*) __get_time_locale (locale)->mon[_REL(ABMON_1)];
232 		break;
233 	case ERA:
234 		ret = (char*) __get_time_locale (locale)->era;
235 		break;
236 	case ERA_D_FMT:
237 		ret = (char*) __get_time_locale (locale)->era_d_fmt;
238 		break;
239 	case ERA_D_T_FMT:
240 		ret = (char*) __get_time_locale (locale)->era_d_t_fmt;
241 		break;
242 	case ERA_T_FMT:
243 		ret = (char*) __get_time_locale (locale)->era_t_fmt;
244 		break;
245 	case ALT_DIGITS:
246 		ret = (char*) __get_time_locale (locale)->alt_digits;
247 		break;
248 	case _DATE_FMT:	/* GNU extension */
249 		ret = (char*) __get_time_locale (locale)->date_fmt;
250 		break;
251 	case RADIXCHAR:
252 		ret = (char*) __get_numeric_locale (locale)->decimal_point;
253 		break;
254 	case THOUSEP:
255 		ret = (char*) __get_numeric_locale (locale)->thousands_sep;
256 		break;
257 	case YESEXPR:
258 		ret = (char*) __get_messages_locale (locale)->yesexpr;
259 		break;
260 	case NOEXPR:
261 		ret = (char*) __get_messages_locale (locale)->noexpr;
262 		break;
263 	/*
264 	 * All items marked with LEGACY are available, but not recomended
265 	 * by SUSv2 to be used in portable applications since they're subject
266 	 * to remove in future specification editions
267 	 */
268 	case YESSTR:            /* LEGACY  */
269 		ret = (char*) __get_messages_locale (locale)->yesstr;
270 		break;
271 	case NOSTR:             /* LEGACY  */
272 		ret = (char*) __get_messages_locale (locale)->nostr;
273 		break;
274 	case CRNCYSTR:
275 		ret = "";
276 		cs = (char*) __get_monetary_locale (locale)->currency_symbol;
277 		if (*cs != '\0') {
278 			char pos = __localeconv_l (locale)->p_cs_precedes;
279 
280 			if (pos == __localeconv_l (locale)->n_cs_precedes) {
281 				char psn = '\0';
282 
283 				if (pos == CHAR_MAX) {
284 					if (strcmp(cs, __get_monetary_locale (locale)->mon_decimal_point) == 0)
285 						psn = '.';
286 				} else
287 					psn = pos ? '-' : '+';
288 				if (psn != '\0') {
289 					int clen = strlen(cs);
290 
291                                         nptr = realloc(csym, clen + 2);
292                                         if (!nptr && csym)
293                                           free (csym);
294 
295                                         csym = nptr;
296 
297 					if (csym != NULL) {
298 						*csym = psn;
299 						strcpy(csym + 1, cs);
300 						ret = csym;
301 					}
302 				}
303 			}
304 		}
305 		break;
306 	case D_MD_ORDER:        /* local extension */
307 		ret = (char *) __get_time_locale (locale)->md_order;
308 		break;
309 #ifdef __HAVE_LOCALE_INFO__
310 	case _NL_CTYPE_MB_CUR_MAX:
311 		ret = (char *) __get_ctype_locale (locale)->mb_cur_max;
312 		break;
313 #endif
314 	default:
315 		/* Relies on the fact that LC_ALL is 0, and all other
316 		   LC_ constants are in ascending order. */
317 		if (item > NL_LOCALE_NAME(LC_ALL)
318 		    && item < NL_LOCALE_NAME(_LC_LAST)) {
319 			return locale->categories[item
320 						  - NL_LOCALE_NAME(LC_ALL)];
321 		}
322 #ifdef __HAVE_LOCALE_INFO_EXTENDED__
323 		if (item > _NL_LOCALE_EXTENDED_FIRST_ENTRY
324 		    && item < _NL_LOCALE_EXTENDED_LAST_ENTRY) {
325 			int idx = item - _NL_LOCALE_EXTENDED_FIRST_ENTRY - 1;
326 			return *(char **) ((char *) (*nl_ext[idx].base)(locale)
327 					   + nl_ext[idx].offset);
328 		}
329 #endif
330 		ret = "";
331    }
332    return (ret);
333 }
334 
nl_langinfo(nl_item item)335 char *nl_langinfo (nl_item item)
336 {
337   return nl_langinfo_l (item, __get_current_locale ());
338 }
339