2002-08-26 Roland McGrath <roland@redhat.com>
[kopensolaris-gnu/glibc.git] / locale / lc-time.c
1 /* Define current locale data for LC_TIME category.
2    Copyright (C) 1995-2001, 2002 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    The GNU C Library 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 GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 #include <bits/libc-lock.h>
21 #include <endian.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <time.h>
25 #include <wchar.h>
26 #include "localeinfo.h"
27
28 _NL_CURRENT_DEFINE (LC_TIME);
29
30 /* Some of the functions here must not be used while setlocale is called.  */
31 __libc_lock_define (extern, __libc_setlocale_lock attribute_hidden)
32
33
34 static int era_initialized;
35
36 static struct era_entry *eras;
37 static size_t num_eras;
38 static int alt_digits_initialized;
39 static const char **alt_digits;
40
41
42 static int walt_digits_initialized;
43 static const wchar_t **walt_digits;
44
45
46 void
47 _nl_postload_time (void)
48 {
49   /* Prepare lazy initialization of `era' and `alt_digits' array.  */
50   era_initialized = 0;
51   alt_digits_initialized = 0;
52   walt_digits_initialized = 0;
53 }
54
55 #define ERA_DATE_CMP(a, b) \
56   (a[0] < b[0] || (a[0] == b[0] && (a[1] < b[1]                               \
57                                     || (a[1] == b[1] && a[2] <= b[2]))))
58
59 static void
60 _nl_init_era_entries (void)
61 {
62   size_t cnt;
63
64   __libc_lock_lock (__libc_setlocale_lock);
65
66   if (era_initialized == 0)
67     {
68       size_t new_num_eras = _NL_CURRENT_WORD (LC_TIME,
69                                               _NL_TIME_ERA_NUM_ENTRIES);
70       if (new_num_eras == 0)
71         {
72           free (eras);
73           eras = NULL;
74         }
75       else
76         {
77           struct era_entry *new_eras = eras;
78
79           if (num_eras != new_num_eras)
80             new_eras =
81               (struct era_entry *) realloc (eras,
82                                             new_num_eras
83                                             * sizeof (struct era_entry));
84           if (new_eras == NULL)
85             {
86               free (eras);
87               num_eras = 0;
88               eras = NULL;
89             }
90           else
91             {
92               const char *ptr = _NL_CURRENT (LC_TIME, _NL_TIME_ERA_ENTRIES);
93               num_eras = new_num_eras;
94               eras = new_eras;
95
96               for (cnt = 0; cnt < num_eras; ++cnt)
97                 {
98                   const char *base_ptr = ptr;
99                   memcpy ((void *) (eras + cnt), (const void *) ptr,
100                           sizeof (uint32_t) * 8);
101
102                   if (ERA_DATE_CMP(eras[cnt].start_date,
103                                    eras[cnt].stop_date))
104                     if (eras[cnt].direction == (uint32_t) '+')
105                       eras[cnt].absolute_direction = 1;
106                     else
107                       eras[cnt].absolute_direction = -1;
108                   else
109                     if (eras[cnt].direction == (uint32_t) '+')
110                       eras[cnt].absolute_direction = -1;
111                     else
112                       eras[cnt].absolute_direction = 1;
113
114                   /* Skip numeric values.  */
115                   ptr += sizeof (uint32_t) * 8;
116
117                   /* Set and skip era name.  */
118                   eras[cnt].era_name = ptr;
119                   ptr = strchr (ptr, '\0') + 1;
120
121                   /* Set and skip era format.  */
122                   eras[cnt].era_format = ptr;
123                   ptr = strchr (ptr, '\0') + 1;
124
125                   ptr += 3 - (((ptr - (const char *) base_ptr) + 3) & 3);
126
127                   /* Set and skip wide era name.  */
128                   eras[cnt].era_wname = (wchar_t *) ptr;
129                   ptr = (char *) (wcschr ((wchar_t *) ptr, L'\0') + 1);
130
131                   /* Set and skip wide era format.  */
132                   eras[cnt].era_wformat = (wchar_t *) ptr;
133                   ptr = (char *) (wcschr ((wchar_t *) ptr, L'\0') + 1);
134                 }
135             }
136         }
137
138       era_initialized = 1;
139     }
140
141   __libc_lock_unlock (__libc_setlocale_lock);
142 }
143
144
145 struct era_entry *
146 _nl_get_era_entry (const struct tm *tp)
147 {
148   struct era_entry *result;
149   int32_t tdate[3];
150   size_t cnt;
151
152   tdate[0] = tp->tm_year;
153   tdate[1] = tp->tm_mon;
154   tdate[2] = tp->tm_mday;
155
156   if (era_initialized == 0)
157     _nl_init_era_entries ();
158
159   /* Now compare date with the available eras.  */
160   for (cnt = 0; cnt < num_eras; ++cnt)
161     if ((ERA_DATE_CMP(eras[cnt].start_date, tdate)
162          && ERA_DATE_CMP(tdate, eras[cnt].stop_date))
163         || (ERA_DATE_CMP(eras[cnt].stop_date, tdate)
164             && ERA_DATE_CMP(tdate, eras[cnt].start_date)))
165       break;
166
167   result = cnt < num_eras ? &eras[cnt] : NULL;
168
169   return result;
170 }
171
172
173 struct era_entry *
174 _nl_select_era_entry (int cnt)
175 {
176   if (era_initialized == 0)
177     _nl_init_era_entries ();
178
179   return &eras[cnt];
180 }
181
182
183 const char *
184 _nl_get_alt_digit (unsigned int number)
185 {
186   const char *result;
187
188   __libc_lock_lock (__libc_setlocale_lock);
189
190   if (alt_digits_initialized == 0)
191     {
192       alt_digits_initialized = 1;
193
194       if (alt_digits == NULL)
195         alt_digits = malloc (100 * sizeof (const char *));
196
197       if (alt_digits != NULL)
198         {
199           const char *ptr = _NL_CURRENT (LC_TIME, ALT_DIGITS);
200           size_t cnt;
201
202           if (alt_digits != NULL)
203             for (cnt = 0; cnt < 100; ++cnt)
204               {
205                 alt_digits[cnt] = ptr;
206
207                 /* Skip digit format. */
208                 ptr = strchr (ptr, '\0') + 1;
209               }
210         }
211     }
212
213   result = alt_digits != NULL && number < 100 ? alt_digits[number] : NULL;
214
215   __libc_lock_unlock (__libc_setlocale_lock);
216
217   return result;
218 }
219
220
221 const wchar_t *
222 _nl_get_walt_digit (unsigned int number)
223 {
224   const wchar_t *result;
225
226   __libc_lock_lock (__libc_setlocale_lock);
227
228   if (walt_digits_initialized == 0)
229     {
230       walt_digits_initialized = 1;
231
232       if (walt_digits == NULL)
233         walt_digits = malloc (100 * sizeof (const uint32_t *));
234
235       if (walt_digits != NULL)
236         {
237           const wchar_t *ptr = _NL_CURRENT_WSTR (LC_TIME, _NL_WALT_DIGITS);
238           size_t cnt;
239
240           for (cnt = 0; cnt < 100; ++cnt)
241             {
242               walt_digits[cnt] = ptr;
243
244               /* Skip digit format. */
245               ptr = wcschr (ptr, L'\0') + 1;
246             }
247         }
248     }
249
250   result = walt_digits != NULL && number < 100 ? walt_digits[number] : NULL;
251
252   __libc_lock_unlock (__libc_setlocale_lock);
253
254   return (wchar_t *) result;
255 }
256
257
258 int
259 _nl_parse_alt_digit (const char **strp)
260 {
261   const char *str = *strp;
262   int result = -1;
263   size_t cnt;
264   size_t maxlen = 0;
265
266   __libc_lock_lock (__libc_setlocale_lock);
267
268   if (alt_digits_initialized == 0)
269     {
270       alt_digits_initialized = 1;
271
272       if (alt_digits == NULL)
273         alt_digits = malloc (100 * sizeof (const char *));
274
275       if (alt_digits != NULL)
276         {
277           const char *ptr = _NL_CURRENT (LC_TIME, ALT_DIGITS);
278
279           if (alt_digits != NULL)
280             for (cnt = 0; cnt < 100; ++cnt)
281               {
282                 alt_digits[cnt] = ptr;
283
284                 /* Skip digit format. */
285                 ptr = strchr (ptr, '\0') + 1;
286               }
287         }
288     }
289
290   /* Matching is not unambiguos.  The alternative digits could be like
291      I, II, III, ... and the first one is a substring of the second
292      and third.  Therefore we must keep on searching until we found
293      the longest possible match.  Note that this is not specified in
294      the standard.  */
295   for (cnt = 0; cnt < 100; ++cnt)
296     {
297       size_t len = strlen (alt_digits[cnt]);
298
299       if (len > maxlen && strncmp (alt_digits[cnt], str, len) == 0)
300         {
301           maxlen = len;
302           result = (int) cnt;
303         }
304     }
305
306   __libc_lock_unlock (__libc_setlocale_lock);
307
308   if (result != -1)
309     *strp += maxlen;
310
311   return result;
312 }
313
314
315 static void
316 free_mem (void)
317 {
318   free (alt_digits);
319   free (walt_digits);
320 }
321 text_set_element (__libc_subfreeres, free_mem);