Update copyright notices with scripts/update-copyrights
[jlayton/glibc.git] / time / alt_digit.c
1 /* Helper functions used by strftime/strptime to handle alternate digits.
2    Copyright (C) 1995-2014 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, see
17    <http://www.gnu.org/licenses/>.  */
18
19 #include "../locale/localeinfo.h"
20 #include <bits/libc-lock.h>
21 #include <stdlib.h>
22 #include <wchar.h>
23 #include <string.h>
24 #include <stdint.h>
25
26 /* Some of the functions here must not be used while setlocale is called.  */
27 __libc_rwlock_define (extern, __libc_setlocale_lock attribute_hidden)
28
29 #define CURRENT(item) (current->values[_NL_ITEM_INDEX (item)].string)
30 #define CURRENT_WSTR(item) \
31   ((wchar_t *) current->values[_NL_ITEM_INDEX (item)].wstr)
32
33 static void
34 _nl_init_alt_digit (struct __locale_data *current)
35 {
36   struct lc_time_data *data;
37
38   if (current->private.time == NULL)
39     {
40       current->private.time = malloc (sizeof *current->private.time);
41       if (current->private.time == NULL)
42         return;
43       memset (current->private.time, 0, sizeof *current->private.time);
44       current->private.cleanup = &_nl_cleanup_time;
45     }
46   data = current->private.time;
47
48   if (! data->alt_digits_initialized)
49     {
50       const char *ptr = CURRENT (ALT_DIGITS);
51       size_t cnt;
52
53       data->alt_digits_initialized = 1;
54
55       if (ptr != NULL)
56         {
57           data->alt_digits = malloc (100 * sizeof (const char *));
58           if (data->alt_digits != NULL)
59             for (cnt = 0; cnt < 100; ++cnt)
60               {
61                 data->alt_digits[cnt] = ptr;
62
63                 /* Skip digit format. */
64                 ptr = strchr (ptr, '\0') + 1;
65               }
66         }
67     }
68
69 }
70
71 const char *
72 internal_function
73 _nl_get_alt_digit (unsigned int number, struct __locale_data *current)
74 {
75   const char *result;
76
77   if (number >= 100 || CURRENT (ALT_DIGITS)[0] == '\0')
78     return NULL;
79
80   __libc_rwlock_wrlock (__libc_setlocale_lock);
81
82   if (current->private.time == NULL
83       || ! current->private.time->alt_digits_initialized)
84     _nl_init_alt_digit (current);
85
86   result = ((current->private.time != NULL
87              && current->private.time->alt_digits != NULL)
88             ? current->private.time->alt_digits[number]
89             : NULL);
90
91   __libc_rwlock_unlock (__libc_setlocale_lock);
92
93   return result;
94 }
95
96
97 const wchar_t *
98 internal_function
99 _nl_get_walt_digit (unsigned int number, struct __locale_data *current)
100 {
101   const wchar_t *result = NULL;
102   struct lc_time_data *data;
103
104   if (number >= 100 || CURRENT_WSTR (_NL_WALT_DIGITS)[0] == L'\0')
105     return NULL;
106
107   __libc_rwlock_wrlock (__libc_setlocale_lock);
108
109   if (current->private.time == NULL)
110     {
111       current->private.time = malloc (sizeof *current->private.time);
112       if (current->private.time == NULL)
113         goto out;
114       memset (current->private.time, 0, sizeof *current->private.time);
115       current->private.cleanup = &_nl_cleanup_time;
116     }
117   data = current->private.time;
118
119   if (! data->walt_digits_initialized)
120     {
121       const wchar_t *ptr = CURRENT_WSTR (_NL_WALT_DIGITS);
122       size_t cnt;
123
124       data->walt_digits_initialized = 1;
125
126       if (ptr != NULL)
127         {
128           data->walt_digits = malloc (100 * sizeof (const uint32_t *));
129           if (data->walt_digits != NULL)
130             for (cnt = 0; cnt < 100; ++cnt)
131               {
132                 data->walt_digits[cnt] = ptr;
133
134                 /* Skip digit format. */
135                 ptr = wcschr (ptr, L'\0') + 1;
136               }
137         }
138     }
139
140   if (data->walt_digits != NULL)
141     result = data->walt_digits[number];
142
143  out:
144   __libc_rwlock_unlock (__libc_setlocale_lock);
145
146   return (wchar_t *) result;
147 }
148
149
150 int
151 internal_function
152 _nl_parse_alt_digit (const char **strp, struct __locale_data *current)
153 {
154   const char *str = *strp;
155   int result = -1;
156   size_t cnt;
157   size_t maxlen = 0;
158
159   if (CURRENT_WSTR (_NL_WALT_DIGITS)[0] == L'\0')
160     return result;
161
162   __libc_rwlock_wrlock (__libc_setlocale_lock);
163
164   if (current->private.time == NULL
165       || ! current->private.time->alt_digits_initialized)
166     _nl_init_alt_digit (current);
167
168   if (current->private.time != NULL &&
169       current->private.time->alt_digits != NULL)
170     /* Matching is not unambiguous.  The alternative digits could be like
171        I, II, III, ... and the first one is a substring of the second
172        and third.  Therefore we must keep on searching until we found
173        the longest possible match.  Note that this is not specified in
174        the standard.  */
175     for (cnt = 0; cnt < 100; ++cnt)
176       {
177         const char *const dig = current->private.time->alt_digits[cnt];
178         size_t len = strlen (dig);
179
180         if (len > maxlen && strncmp (dig, str, len) == 0)
181           {
182             maxlen = len;
183             result = (int) cnt;
184           }
185       }
186
187   __libc_rwlock_unlock (__libc_setlocale_lock);
188
189   if (result != -1)
190     *strp += maxlen;
191
192   return result;
193 }