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