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