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[0]),
82   _NLITEM (time, wmon[1]),
83   _NLITEM (time, wmon[2]),
84   _NLITEM (time, wmon[3]),
85   _NLITEM (time, wmon[4]),
86   _NLITEM (time, wmon[5]),
87   _NLITEM (time, wmon[6]),
88   _NLITEM (time, wmon[7]),
89   _NLITEM (time, wmon[8]),
90   _NLITEM (time, wmon[9]),
91   _NLITEM (time, wmon[10]),
92   _NLITEM (time, wmon[11]),
93   _NLITEM (time, wmonth[0]),
94   _NLITEM (time, wmonth[1]),
95   _NLITEM (time, wmonth[2]),
96   _NLITEM (time, wmonth[3]),
97   _NLITEM (time, wmonth[4]),
98   _NLITEM (time, wmonth[5]),
99   _NLITEM (time, wmonth[6]),
100   _NLITEM (time, wmonth[7]),
101   _NLITEM (time, wmonth[8]),
102   _NLITEM (time, wmonth[9]),
103   _NLITEM (time, wmonth[10]),
104   _NLITEM (time, wmonth[11]),
105   _NLITEM (time, wwday[0]),
106   _NLITEM (time, wwday[1]),
107   _NLITEM (time, wwday[2]),
108   _NLITEM (time, wwday[3]),
109   _NLITEM (time, wwday[4]),
110   _NLITEM (time, wwday[5]),
111   _NLITEM (time, wwday[6]),
112   _NLITEM (time, wweekday[0]),
113   _NLITEM (time, wweekday[1]),
114   _NLITEM (time, wweekday[2]),
115   _NLITEM (time, wweekday[3]),
116   _NLITEM (time, wweekday[4]),
117   _NLITEM (time, wweekday[5]),
118   _NLITEM (time, wweekday[6]),
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