1 /* Helper functions used by strftime/strptime to handle alternate digits.
2    Copyright (C) 1995-2021 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    <https://www.gnu.org/licenses/>.  */
18 
19 #include "../locale/localeinfo.h"
20 #include <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.  */
__libc_rwlock_define(extern,__libc_setlocale_lock attribute_hidden)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 *
_nl_get_alt_digit(unsigned int number,struct __locale_data * current)72 _nl_get_alt_digit (unsigned int number, struct __locale_data *current)
73 {
74   const char *result;
75 
76   if (number >= 100 || CURRENT (ALT_DIGITS)[0] == '\0')
77     return NULL;
78 
79   __libc_rwlock_wrlock (__libc_setlocale_lock);
80 
81   if (current->private.time == NULL
82       || ! current->private.time->alt_digits_initialized)
83     _nl_init_alt_digit (current);
84 
85   result = ((current->private.time != NULL
86 	     && current->private.time->alt_digits != NULL)
87 	    ? current->private.time->alt_digits[number]
88 	    : NULL);
89 
90   __libc_rwlock_unlock (__libc_setlocale_lock);
91 
92   return result;
93 }
94 
95 
96 const wchar_t *
_nl_get_walt_digit(unsigned int number,struct __locale_data * current)97 _nl_get_walt_digit (unsigned int number, struct __locale_data *current)
98 {
99   const wchar_t *result = NULL;
100   struct lc_time_data *data;
101 
102   if (number >= 100 || CURRENT_WSTR (_NL_WALT_DIGITS)[0] == L'\0')
103     return NULL;
104 
105   __libc_rwlock_wrlock (__libc_setlocale_lock);
106 
107   if (current->private.time == NULL)
108     {
109       current->private.time = malloc (sizeof *current->private.time);
110       if (current->private.time == NULL)
111 	goto out;
112       memset (current->private.time, 0, sizeof *current->private.time);
113       current->private.cleanup = &_nl_cleanup_time;
114     }
115   data = current->private.time;
116 
117   if (! data->walt_digits_initialized)
118     {
119       const wchar_t *ptr = CURRENT_WSTR (_NL_WALT_DIGITS);
120       size_t cnt;
121 
122       data->walt_digits_initialized = 1;
123 
124       if (ptr != NULL)
125 	{
126 	  data->walt_digits = malloc (100 * sizeof (const uint32_t *));
127 	  if (data->walt_digits != NULL)
128 	    for (cnt = 0; cnt < 100; ++cnt)
129 	      {
130 		data->walt_digits[cnt] = ptr;
131 
132 		/* Skip digit format. */
133 		ptr = __wcschr (ptr, L'\0') + 1;
134 	      }
135 	}
136     }
137 
138   if (data->walt_digits != NULL)
139     result = data->walt_digits[number];
140 
141  out:
142   __libc_rwlock_unlock (__libc_setlocale_lock);
143 
144   return (wchar_t *) result;
145 }
146 
147 
148 int
_nl_parse_alt_digit(const char ** strp,struct __locale_data * current)149 _nl_parse_alt_digit (const char **strp, struct __locale_data *current)
150 {
151   const char *str = *strp;
152   int result = -1;
153   size_t cnt;
154   size_t maxlen = 0;
155 
156   if (CURRENT_WSTR (_NL_WALT_DIGITS)[0] == L'\0')
157     return result;
158 
159   __libc_rwlock_wrlock (__libc_setlocale_lock);
160 
161   if (current->private.time == NULL
162       || ! current->private.time->alt_digits_initialized)
163     _nl_init_alt_digit (current);
164 
165   if (current->private.time != NULL
166       && current->private.time->alt_digits != NULL)
167     /* Matching is not unambiguous.  The alternative digits could be like
168        I, II, III, ... and the first one is a substring of the second
169        and third.  Therefore we must keep on searching until we found
170        the longest possible match.  Note that this is not specified in
171        the standard.  */
172     for (cnt = 0; cnt < 100; ++cnt)
173       {
174 	const char *const dig = current->private.time->alt_digits[cnt];
175 	size_t len = strlen (dig);
176 
177 	if (len > maxlen && strncmp (dig, str, len) == 0)
178 	  {
179 	    maxlen = len;
180 	    result = (int) cnt;
181 	  }
182       }
183 
184   __libc_rwlock_unlock (__libc_setlocale_lock);
185 
186   if (result != -1)
187     *strp += maxlen;
188 
189   return result;
190 }
191