Bump to 1.14.1
[platform/upstream/augeas.git] / lib / nl_langinfo.c
1 /* nl_langinfo() replacement: query locale dependent information.
2
3    Copyright (C) 2007-2016 Free Software Foundation, Inc.
4
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17
18 #include <config.h>
19
20 /* Specification.  */
21 #include <langinfo.h>
22
23 #include <locale.h>
24 #include <string.h>
25 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
26 # define WIN32_LEAN_AND_MEAN  /* avoid including junk */
27 # include <windows.h>
28 # include <stdio.h>
29 #endif
30
31 /* Return the codeset of the current locale, if this is easily deducible.
32    Otherwise, return "".  */
33 static char *
34 ctype_codeset (void)
35 {
36   static char buf[2 + 10 + 1];
37   char const *locale = setlocale (LC_CTYPE, NULL);
38   char *codeset = buf;
39   size_t codesetlen;
40   codeset[0] = '\0';
41
42   if (locale && locale[0])
43     {
44       /* If the locale name contains an encoding after the dot, return it.  */
45       char *dot = strchr (locale, '.');
46
47       if (dot)
48         {
49           /* Look for the possible @... trailer and remove it, if any.  */
50           char *codeset_start = dot + 1;
51           char const *modifier = strchr (codeset_start, '@');
52
53           if (! modifier)
54             codeset = codeset_start;
55           else
56             {
57               codesetlen = modifier - codeset_start;
58               if (codesetlen < sizeof buf)
59                 {
60                   codeset = memcpy (buf, codeset_start, codesetlen);
61                   codeset[codesetlen] = '\0';
62                 }
63             }
64         }
65     }
66
67 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
68   /* If setlocale is successful, it returns the number of the
69      codepage, as a string.  Otherwise, fall back on Windows API
70      GetACP, which returns the locale's codepage as a number (although
71      this doesn't change according to what the 'setlocale' call specified).
72      Either way, prepend "CP" to make it a valid codeset name.  */
73   codesetlen = strlen (codeset);
74   if (0 < codesetlen && codesetlen < sizeof buf - 2)
75     memmove (buf + 2, codeset, codesetlen + 1);
76   else
77     sprintf (buf + 2, "%u", GetACP ());
78   codeset = memcpy (buf, "CP", 2);
79 #endif
80   return codeset;
81 }
82
83
84 #if REPLACE_NL_LANGINFO
85
86 /* Override nl_langinfo with support for added nl_item values.  */
87
88 # undef nl_langinfo
89
90 char *
91 rpl_nl_langinfo (nl_item item)
92 {
93   switch (item)
94     {
95 # if GNULIB_defined_CODESET
96     case CODESET:
97       return ctype_codeset ();
98 # endif
99 # if GNULIB_defined_T_FMT_AMPM
100     case T_FMT_AMPM:
101       return (char *) "%I:%M:%S %p";
102 # endif
103 # if GNULIB_defined_ERA
104     case ERA:
105       /* The format is not standardized.  In glibc it is a sequence of strings
106          of the form "direction:offset:start_date:end_date:era_name:era_format"
107          with an empty string at the end.  */
108       return (char *) "";
109     case ERA_D_FMT:
110       /* The %Ex conversion in strftime behaves like %x if the locale does not
111          have an alternative time format.  */
112       item = D_FMT;
113       break;
114     case ERA_D_T_FMT:
115       /* The %Ec conversion in strftime behaves like %c if the locale does not
116          have an alternative time format.  */
117       item = D_T_FMT;
118       break;
119     case ERA_T_FMT:
120       /* The %EX conversion in strftime behaves like %X if the locale does not
121          have an alternative time format.  */
122       item = T_FMT;
123       break;
124     case ALT_DIGITS:
125       /* The format is not standardized.  In glibc it is a sequence of 10
126          strings, appended in memory.  */
127       return (char *) "\0\0\0\0\0\0\0\0\0\0";
128 # endif
129 # if GNULIB_defined_YESEXPR || !FUNC_NL_LANGINFO_YESEXPR_WORKS
130     case YESEXPR:
131       return (char *) "^[yY]";
132     case NOEXPR:
133       return (char *) "^[nN]";
134 # endif
135     default:
136       break;
137     }
138   return nl_langinfo (item);
139 }
140
141 #else
142
143 /* Provide nl_langinfo from scratch, either for native MS-Windows, or
144    for old Unix platforms without locales, such as Linux libc5 or
145    BeOS.  */
146
147 # include <time.h>
148
149 char *
150 nl_langinfo (nl_item item)
151 {
152   static char nlbuf[100];
153   struct tm tmm = { 0 };
154
155   switch (item)
156     {
157     /* nl_langinfo items of the LC_CTYPE category */
158     case CODESET:
159       {
160         char *codeset = ctype_codeset ();
161         if (*codeset)
162           return codeset;
163       }
164 # ifdef __BEOS__
165       return (char *) "UTF-8";
166 # else
167       return (char *) "ISO-8859-1";
168 # endif
169     /* nl_langinfo items of the LC_NUMERIC category */
170     case RADIXCHAR:
171       return localeconv () ->decimal_point;
172     case THOUSEP:
173       return localeconv () ->thousands_sep;
174     case GROUPING:
175       return localeconv () ->grouping;
176     /* nl_langinfo items of the LC_TIME category.
177        TODO: Really use the locale.  */
178     case D_T_FMT:
179     case ERA_D_T_FMT:
180       return (char *) "%a %b %e %H:%M:%S %Y";
181     case D_FMT:
182     case ERA_D_FMT:
183       return (char *) "%m/%d/%y";
184     case T_FMT:
185     case ERA_T_FMT:
186       return (char *) "%H:%M:%S";
187     case T_FMT_AMPM:
188       return (char *) "%I:%M:%S %p";
189     case AM_STR:
190       if (!strftime (nlbuf, sizeof nlbuf, "%p", &tmm))
191         return (char *) "AM";
192       return nlbuf;
193     case PM_STR:
194       tmm.tm_hour = 12;
195       if (!strftime (nlbuf, sizeof nlbuf, "%p", &tmm))
196         return (char *) "PM";
197       return nlbuf;
198     case DAY_1:
199     case DAY_2:
200     case DAY_3:
201     case DAY_4:
202     case DAY_5:
203     case DAY_6:
204     case DAY_7:
205       {
206         static char const days[][sizeof "Wednesday"] = {
207           "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
208           "Friday", "Saturday"
209         };
210         tmm.tm_wday = item - DAY_1;
211         if (!strftime (nlbuf, sizeof nlbuf, "%A", &tmm))
212           return (char *) days[item - DAY_1];
213         return nlbuf;
214       }
215     case ABDAY_1:
216     case ABDAY_2:
217     case ABDAY_3:
218     case ABDAY_4:
219     case ABDAY_5:
220     case ABDAY_6:
221     case ABDAY_7:
222       {
223         static char const abdays[][sizeof "Sun"] = {
224           "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
225         };
226         tmm.tm_wday = item - ABDAY_1;
227         if (!strftime (nlbuf, sizeof nlbuf, "%a", &tmm))
228           return (char *) abdays[item - ABDAY_1];
229         return nlbuf;
230       }
231     case MON_1:
232     case MON_2:
233     case MON_3:
234     case MON_4:
235     case MON_5:
236     case MON_6:
237     case MON_7:
238     case MON_8:
239     case MON_9:
240     case MON_10:
241     case MON_11:
242     case MON_12:
243       {
244         static char const months[][sizeof "September"] = {
245           "January", "February", "March", "April", "May", "June", "July",
246           "September", "October", "November", "December"
247         };
248         tmm.tm_mon = item - MON_1;
249         if (!strftime (nlbuf, sizeof nlbuf, "%B", &tmm))
250           return (char *) months[item - MON_1];
251         return nlbuf;
252       }
253     case ABMON_1:
254     case ABMON_2:
255     case ABMON_3:
256     case ABMON_4:
257     case ABMON_5:
258     case ABMON_6:
259     case ABMON_7:
260     case ABMON_8:
261     case ABMON_9:
262     case ABMON_10:
263     case ABMON_11:
264     case ABMON_12:
265       {
266         static char const abmonths[][sizeof "Jan"] = {
267           "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
268           "Sep", "Oct", "Nov", "Dec"
269         };
270         tmm.tm_mon = item - ABMON_1;
271         if (!strftime (nlbuf, sizeof nlbuf, "%b", &tmm))
272           return (char *) abmonths[item - ABMON_1];
273         return nlbuf;
274       }
275     case ERA:
276       return (char *) "";
277     case ALT_DIGITS:
278       return (char *) "\0\0\0\0\0\0\0\0\0\0";
279     /* nl_langinfo items of the LC_MONETARY category.  */
280     case CRNCYSTR:
281       return localeconv () ->currency_symbol;
282     case INT_CURR_SYMBOL:
283       return localeconv () ->int_curr_symbol;
284     case MON_DECIMAL_POINT:
285       return localeconv () ->mon_decimal_point;
286     case MON_THOUSANDS_SEP:
287       return localeconv () ->mon_thousands_sep;
288     case MON_GROUPING:
289       return localeconv () ->mon_grouping;
290     case POSITIVE_SIGN:
291       return localeconv () ->positive_sign;
292     case NEGATIVE_SIGN:
293       return localeconv () ->negative_sign;
294     case FRAC_DIGITS:
295       return & localeconv () ->frac_digits;
296     case INT_FRAC_DIGITS:
297       return & localeconv () ->int_frac_digits;
298     case P_CS_PRECEDES:
299       return & localeconv () ->p_cs_precedes;
300     case N_CS_PRECEDES:
301       return & localeconv () ->n_cs_precedes;
302     case P_SEP_BY_SPACE:
303       return & localeconv () ->p_sep_by_space;
304     case N_SEP_BY_SPACE:
305       return & localeconv () ->n_sep_by_space;
306     case P_SIGN_POSN:
307       return & localeconv () ->p_sign_posn;
308     case N_SIGN_POSN:
309       return & localeconv () ->n_sign_posn;
310     /* nl_langinfo items of the LC_MESSAGES category
311        TODO: Really use the locale. */
312     case YESEXPR:
313       return (char *) "^[yY]";
314     case NOEXPR:
315       return (char *) "^[nN]";
316     default:
317       return (char *) "";
318     }
319 }
320
321 #endif