]>
Commit | Line | Data |
---|---|---|
f76d7052 | 1 | /* Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc. |
6d52618b | 2 | This file is part of the GNU C Library. |
5290baf0 | 3 | Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995. |
19bc17a9 | 4 | |
6d52618b UD |
5 | The GNU C Library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Library General Public License as | |
7 | published by the Free Software Foundation; either version 2 of the | |
8 | License, or (at your option) any later version. | |
19bc17a9 | 9 | |
6d52618b UD |
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 | Library General Public License for more details. | |
19bc17a9 | 14 | |
6d52618b UD |
15 | You should have received a copy of the GNU Library General Public |
16 | License along with the GNU C Library; see the file COPYING.LIB. If not, | |
17 | write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
18 | Boston, MA 02111-1307, USA. */ | |
19bc17a9 RM |
19 | |
20 | #ifdef HAVE_CONFIG_H | |
21 | # include <config.h> | |
22 | #endif | |
23 | ||
24 | #include <langinfo.h> | |
25 | #include <string.h> | |
4360eafd | 26 | #include <libintl.h> |
19bc17a9 RM |
27 | |
28 | /* Undefine following line in production version. */ | |
29 | /* #define NDEBUG 1 */ | |
30 | #include <assert.h> | |
c4029823 | 31 | #include <stdlib.h> |
19bc17a9 RM |
32 | |
33 | #include "locales.h" | |
34 | #include "localeinfo.h" | |
35 | #include "stringtrans.h" | |
36 | ||
c4029823 UD |
37 | #define SWAPU32(w) \ |
38 | (((w) << 24) | (((w) & 0xff00) << 8) | (((w) >> 8) & 0xff00) | ((w) >> 24)) | |
39 | ||
19bc17a9 | 40 | |
880f421f UD |
41 | extern void *xmalloc (size_t __n); |
42 | extern void *xrealloc (void *__p, size_t __n); | |
19bc17a9 RM |
43 | |
44 | ||
c4029823 UD |
45 | /* Entry describing an entry of the era specification. */ |
46 | struct era_data | |
47 | { | |
48 | int32_t direction; | |
49 | int32_t offset; | |
50 | int32_t start_date[3]; | |
51 | int32_t stop_date[3]; | |
52 | const char *name; | |
53 | const char *format; | |
54 | }; | |
55 | ||
56 | ||
19bc17a9 RM |
57 | /* The real definition of the struct for the LC_TIME locale. */ |
58 | struct locale_time_t | |
59 | { | |
60 | const char *abday[7]; | |
61 | size_t cur_num_abday; | |
62 | const char *day[7]; | |
63 | size_t cur_num_day; | |
64 | const char *abmon[12]; | |
65 | size_t cur_num_abmon; | |
66 | const char *mon[12]; | |
67 | size_t cur_num_mon; | |
68 | const char *am_pm[2]; | |
69 | size_t cur_num_am_pm; | |
70 | const char *d_t_fmt; | |
71 | const char *d_fmt; | |
72 | const char *t_fmt; | |
73 | const char *t_fmt_ampm; | |
ec4b0518 | 74 | const char **era; |
c4029823 | 75 | u_int32_t cur_num_era; |
19bc17a9 RM |
76 | const char *era_year; |
77 | const char *era_d_t_fmt; | |
78 | const char *era_t_fmt; | |
79 | const char *era_d_fmt; | |
80 | const char *alt_digits[100]; | |
c4029823 UD |
81 | u_int32_t cur_num_alt_digits; |
82 | ||
83 | struct era_data *era_entries; | |
84 | struct era_data *era_entries_ob; | |
19bc17a9 RM |
85 | }; |
86 | ||
87 | ||
88 | void | |
89 | time_startup (struct linereader *lr, struct localedef_t *locale, | |
90 | struct charset_t *charset) | |
91 | { | |
92 | struct locale_time_t *time; | |
93 | ||
816e6eb5 UD |
94 | /* We have a definition for LC_TIME. */ |
95 | copy_posix.mask &= ~(1 << LC_TIME); | |
96 | ||
19bc17a9 RM |
97 | /* It is important that we always use UCS1 encoding for strings now. */ |
98 | encoding_method = ENC_UCS1; | |
99 | ||
100 | locale->categories[LC_TIME].time = time = | |
101 | (struct locale_time_t *) xmalloc (sizeof (struct locale_time_t)); | |
102 | ||
103 | memset (time, '\0', sizeof (struct locale_time_t)); | |
104 | } | |
105 | ||
106 | ||
107 | void | |
108 | time_finish (struct localedef_t *locale) | |
109 | { | |
110 | struct locale_time_t *time = locale->categories[LC_TIME].time; | |
111 | ||
112 | #define TESTARR_ELEM(cat, max) \ | |
c84142e8 | 113 | if (time->cur_num_##cat == 0 && !be_quiet) \ |
f1f0edfe | 114 | error (0, 0, _("field `%s' in category `%s' undefined"), \ |
19bc17a9 | 115 | #cat, "LC_TIME"); \ |
c84142e8 | 116 | else if (time->cur_num_##cat != max && !be_quiet) \ |
19bc17a9 RM |
117 | error (0, 0, _("field `%s' in category `%s' has not enough values"), \ |
118 | #cat, "LC_TIME") | |
119 | ||
120 | TESTARR_ELEM (abday, 7); | |
121 | TESTARR_ELEM (day, 7); | |
122 | TESTARR_ELEM (abmon, 12); | |
123 | TESTARR_ELEM (mon, 12); | |
124 | TESTARR_ELEM (am_pm, 2); | |
125 | ||
126 | #define TEST_ELEM(cat) \ | |
c84142e8 | 127 | if (time->cat == NULL && !be_quiet) \ |
f1f0edfe | 128 | error (0, 0, _("field `%s' in category `%s' undefined"), \ |
19bc17a9 RM |
129 | #cat, "LC_TIME") |
130 | ||
131 | TEST_ELEM (d_t_fmt); | |
132 | TEST_ELEM (d_fmt); | |
133 | TEST_ELEM (t_fmt); | |
5a97622d UD |
134 | |
135 | /* According to C.Y.Alexis Cheng <alexis@vnet.ibm.com> the T_FMT_AMPM | |
136 | field is optional. */ | |
137 | if (time->t_fmt_ampm == NULL) | |
138 | /* Use the 24h format as default. */ | |
139 | time->t_fmt_ampm = time->t_fmt; | |
c4029823 UD |
140 | |
141 | /* Now process the era entries. */ | |
142 | if (time->cur_num_era != 0) | |
143 | { | |
144 | const int days_per_month[12] = { 31, 29, 31, 30, 31, 30, | |
145 | 31, 31, 30, 31 ,30, 31 }; | |
146 | size_t idx; | |
147 | ||
148 | time->era_entries = | |
149 | (struct era_data *) xmalloc (time->cur_num_era | |
150 | * sizeof (struct era_data)); | |
151 | ||
152 | for (idx = 0; idx < time->cur_num_era; ++idx) | |
153 | { | |
154 | size_t era_len = strlen (time->era[idx]); | |
155 | char *str = xmalloc ((era_len + 1 + 3) & ~3); | |
156 | char *endp; | |
157 | ||
158 | memcpy (str, time->era[idx], era_len + 1); | |
159 | ||
160 | /* First character must be + or - for the direction. */ | |
880f421f | 161 | if (*str != '+' && *str != '-') |
c4029823 | 162 | { |
880f421f UD |
163 | if (!be_quiet) |
164 | error (0, 0, _("direction flag in string %d in `era' field" | |
165 | " in category `%s' is not '+' nor '-'"), | |
166 | idx + 1, "LC_TIME"); | |
c4029823 UD |
167 | /* Default arbitrarily to '+'. */ |
168 | time->era_entries[idx].direction = '+'; | |
169 | } | |
170 | else | |
171 | time->era_entries[idx].direction = *str; | |
880f421f | 172 | if (*++str != ':') |
c4029823 | 173 | { |
880f421f UD |
174 | if (!be_quiet) |
175 | error (0, 0, _("direction flag in string %d in `era' field" | |
176 | " in category `%s' is not a single character"), | |
177 | idx + 1, "LC_TIME"); | |
c4029823 UD |
178 | (void) strsep (&str, ":"); |
179 | } | |
180 | else | |
181 | ++str; | |
182 | ||
183 | /* Now the offset year. */ | |
184 | time->era_entries[idx].offset = strtol (str, &endp, 10); | |
880f421f | 185 | if (endp == str) |
c4029823 | 186 | { |
880f421f UD |
187 | if (!be_quiet) |
188 | error (0, 0, _("illegal number for offset in string %d in" | |
189 | " `era' field in category `%s'"), | |
190 | idx + 1, "LC_TIME"); | |
c4029823 UD |
191 | (void) strsep (&str, ":"); |
192 | } | |
880f421f | 193 | else if (*endp != ':') |
c4029823 | 194 | { |
880f421f UD |
195 | if (!be_quiet) |
196 | error (0, 0, _("garbage at end of offset value in string %d in" | |
197 | " `era' field in category `%s'"), | |
198 | idx + 1, "LC_TIME"); | |
c4029823 UD |
199 | (void) strsep (&str, ":"); |
200 | } | |
201 | else | |
202 | str = endp + 1; | |
203 | ||
204 | /* Next is the starting date in ISO format. */ | |
205 | if (strncmp (str, "-*", 2) == 0) | |
206 | { | |
207 | time->era_entries[idx].start_date[0] = | |
208 | time->era_entries[idx].start_date[1] = | |
209 | time->era_entries[idx].start_date[2] = 0x80000000; | |
210 | if (str[2] != ':') | |
211 | goto garbage_start_date; | |
212 | str += 3; | |
213 | } | |
214 | else if (strncmp (str, "+*", 2) == 0) | |
215 | { | |
216 | time->era_entries[idx].start_date[0] = | |
217 | time->era_entries[idx].start_date[1] = | |
218 | time->era_entries[idx].start_date[2] = 0x7fffffff; | |
219 | if (str[2] != ':') | |
220 | goto garbage_start_date; | |
221 | str += 3; | |
222 | } | |
223 | else | |
224 | { | |
225 | time->era_entries[idx].start_date[0] = strtol (str, &endp, 10); | |
226 | if (endp == str || *endp != '/') | |
227 | goto invalid_start_date; | |
228 | else | |
229 | str = endp + 1; | |
230 | time->era_entries[idx].start_date[0] -= 1900; | |
231 | ||
232 | time->era_entries[idx].start_date[1] = strtol (str, &endp, 10); | |
233 | if (endp == str || *endp != '/') | |
234 | goto invalid_start_date; | |
235 | else | |
236 | str = endp + 1; | |
237 | time->era_entries[idx].start_date[1] -= 1; | |
238 | ||
239 | time->era_entries[idx].start_date[2] = strtol (str, &endp, 10); | |
880f421f | 240 | if (endp == str) |
c4029823 UD |
241 | { |
242 | invalid_start_date: | |
880f421f UD |
243 | if (!be_quiet) |
244 | error (0, 0, _("illegal starting date in string %d in" | |
245 | " `era' field in category `%s'"), | |
246 | idx + 1, "LC_TIME"); | |
c4029823 UD |
247 | (void) strsep (&str, ":"); |
248 | } | |
880f421f | 249 | else if (*endp != ':') |
c4029823 UD |
250 | { |
251 | garbage_start_date: | |
880f421f UD |
252 | if (!be_quiet) |
253 | error (0, 0, _("garbage at end of starting date " | |
254 | "in string %d in `era' field " | |
255 | "in category `%s'"), | |
256 | idx + 1, "LC_TIME"); | |
c4029823 UD |
257 | (void) strsep (&str, ":"); |
258 | } | |
259 | else | |
260 | { | |
261 | str = endp + 1; | |
262 | ||
263 | /* Check for valid value. */ | |
c84142e8 UD |
264 | if ((time->era_entries[idx].start_date[1] < 0 |
265 | || time->era_entries[idx].start_date[1] >= 12 | |
266 | || time->era_entries[idx].start_date[2] < 0 | |
267 | || (time->era_entries[idx].start_date[2] | |
268 | > days_per_month[time->era_entries[idx].start_date[1]]) | |
269 | || (time->era_entries[idx].start_date[1] == 2 | |
270 | && time->era_entries[idx].start_date[2] == 29 | |
271 | && !__isleap (time->era_entries[idx].start_date[0]))) | |
272 | && !be_quiet) | |
c4029823 UD |
273 | error (0, 0, _("starting date is illegal in" |
274 | " string %d in `era' field in" | |
275 | " category `%s'"), | |
276 | idx + 1, "LC_TIME"); | |
277 | } | |
278 | } | |
279 | ||
6d52618b | 280 | /* Next is the stopping date in ISO format. */ |
c4029823 UD |
281 | if (strncmp (str, "-*", 2) == 0) |
282 | { | |
283 | time->era_entries[idx].stop_date[0] = | |
284 | time->era_entries[idx].stop_date[1] = | |
285 | time->era_entries[idx].stop_date[2] = 0x80000000; | |
286 | if (str[2] != ':') | |
287 | goto garbage_stop_date; | |
288 | str += 3; | |
289 | } | |
290 | else if (strncmp (str, "+*", 2) == 0) | |
291 | { | |
292 | time->era_entries[idx].stop_date[0] = | |
293 | time->era_entries[idx].stop_date[1] = | |
294 | time->era_entries[idx].stop_date[2] = 0x7fffffff; | |
295 | if (str[2] != ':') | |
296 | goto garbage_stop_date; | |
297 | str += 3; | |
298 | } | |
299 | else | |
300 | { | |
301 | time->era_entries[idx].stop_date[0] = strtol (str, &endp, 10); | |
302 | if (endp == str || *endp != '/') | |
303 | goto invalid_stop_date; | |
304 | else | |
305 | str = endp + 1; | |
306 | time->era_entries[idx].stop_date[0] -= 1900; | |
307 | ||
308 | time->era_entries[idx].stop_date[1] = strtol (str, &endp, 10); | |
309 | if (endp == str || *endp != '/') | |
310 | goto invalid_stop_date; | |
311 | else | |
312 | str = endp + 1; | |
313 | time->era_entries[idx].stop_date[1] -= 1; | |
314 | ||
315 | time->era_entries[idx].stop_date[2] = strtol (str, &endp, 10); | |
880f421f | 316 | if (endp == str) |
c4029823 UD |
317 | { |
318 | invalid_stop_date: | |
880f421f UD |
319 | if (!be_quiet) |
320 | error (0, 0, _("illegal stopping date in string %d in" | |
321 | " `era' field in category `%s'"), | |
322 | idx + 1, "LC_TIME"); | |
c4029823 UD |
323 | (void) strsep (&str, ":"); |
324 | } | |
880f421f | 325 | else if (*endp != ':') |
c4029823 UD |
326 | { |
327 | garbage_stop_date: | |
880f421f UD |
328 | if (!be_quiet) |
329 | error (0, 0, _("garbage at end of stopping date " | |
330 | "in string %d in `era' field " | |
331 | "in category `%s'"), | |
332 | idx + 1, "LC_TIME"); | |
c4029823 UD |
333 | (void) strsep (&str, ":"); |
334 | } | |
335 | else | |
336 | { | |
337 | str = endp + 1; | |
338 | ||
339 | /* Check for valid value. */ | |
c84142e8 UD |
340 | if ((time->era_entries[idx].stop_date[1] < 0 |
341 | || time->era_entries[idx].stop_date[1] >= 12 | |
342 | || time->era_entries[idx].stop_date[2] < 0 | |
343 | || (time->era_entries[idx].stop_date[2] | |
344 | > days_per_month[time->era_entries[idx].stop_date[1]]) | |
345 | || (time->era_entries[idx].stop_date[1] == 2 | |
346 | && time->era_entries[idx].stop_date[2] == 29 | |
347 | && !__isleap (time->era_entries[idx].stop_date[0]))) | |
348 | && !be_quiet) | |
c4029823 UD |
349 | error (0, 0, _("stopping date is illegal in" |
350 | " string %d in `era' field in" | |
351 | " category `%s'"), | |
352 | idx + 1, "LC_TIME"); | |
353 | } | |
354 | } | |
355 | ||
880f421f | 356 | if (str == NULL || *str == '\0') |
c4029823 | 357 | { |
880f421f UD |
358 | if (!be_quiet) |
359 | error (0, 0, _("missing era name in string %d in `era' field" | |
360 | " in category `%s'"), idx + 1, "LC_TIME"); | |
f76d7052 UD |
361 | /* Make sure that name and format are adjacent strings |
362 | in memory. */ | |
363 | time->era_entries[idx].name = "\0"; | |
364 | time->era_entries[idx].format | |
365 | = time->era_entries[idx].name + 1; | |
c4029823 UD |
366 | } |
367 | else | |
368 | { | |
369 | time->era_entries[idx].name = strsep (&str, ":"); | |
370 | ||
880f421f | 371 | if (str == NULL || *str == '\0') |
c4029823 | 372 | { |
880f421f UD |
373 | if (!be_quiet) |
374 | error (0, 0, _("missing era format in string %d in `era'" | |
375 | " field in category `%s'"), | |
376 | idx + 1, "LC_TIME"); | |
f76d7052 UD |
377 | /* Make sure that name and format are adjacent strings |
378 | in memory. */ | |
379 | time->era_entries[idx].name = "\0"; | |
380 | time->era_entries[idx].format | |
381 | = time->era_entries[idx].name + 1; | |
c4029823 UD |
382 | } |
383 | else | |
384 | time->era_entries[idx].format = str; | |
385 | } | |
386 | } | |
387 | ||
388 | /* Construct the array for the other byte order. */ | |
389 | time->era_entries_ob = | |
390 | (struct era_data *) xmalloc (time->cur_num_era | |
391 | * sizeof (struct era_data)); | |
392 | ||
393 | for (idx = 0; idx < time->cur_num_era; ++idx) | |
394 | { | |
395 | time->era_entries_ob[idx].direction = | |
396 | SWAPU32 (time->era_entries[idx].direction); | |
397 | time->era_entries_ob[idx].offset = | |
398 | SWAPU32 (time->era_entries[idx].offset); | |
399 | time->era_entries_ob[idx].start_date[0] = | |
400 | SWAPU32 (time->era_entries[idx].start_date[0]); | |
401 | time->era_entries_ob[idx].start_date[1] = | |
402 | SWAPU32 (time->era_entries[idx].start_date[1]); | |
403 | time->era_entries_ob[idx].start_date[2] = | |
404 | SWAPU32 (time->era_entries[idx].stop_date[2]); | |
405 | time->era_entries_ob[idx].stop_date[0] = | |
406 | SWAPU32 (time->era_entries[idx].stop_date[0]); | |
407 | time->era_entries_ob[idx].stop_date[1] = | |
408 | SWAPU32 (time->era_entries[idx].stop_date[1]); | |
409 | time->era_entries_ob[idx].stop_date[2] = | |
410 | SWAPU32 (time->era_entries[idx].stop_date[2]); | |
411 | time->era_entries_ob[idx].name = | |
412 | time->era_entries[idx].name; | |
413 | time->era_entries_ob[idx].format = | |
414 | time->era_entries[idx].format; | |
415 | } | |
416 | } | |
19bc17a9 RM |
417 | } |
418 | ||
419 | ||
420 | void | |
421 | time_output (struct localedef_t *locale, const char *output_path) | |
422 | { | |
423 | struct locale_time_t *time = locale->categories[LC_TIME].time; | |
424 | struct iovec iov[2 + _NL_ITEM_INDEX (_NL_NUM_LC_TIME) | |
c4029823 UD |
425 | + time->cur_num_era - 1 |
426 | + time->cur_num_alt_digits - 1 | |
a68b0d31 UD |
427 | + 1 + (time->cur_num_era * 9 - 1) * 2 |
428 | + (time->cur_num_era == 0)]; | |
19bc17a9 | 429 | struct locale_file data; |
7a12c6bb | 430 | u_int32_t idx[_NL_ITEM_INDEX (_NL_NUM_LC_TIME)]; |
19bc17a9 RM |
431 | size_t cnt, last_idx, num; |
432 | ||
433 | if ((locale->binary & (1 << LC_TIME)) != 0) | |
434 | { | |
435 | iov[0].iov_base = time; | |
436 | iov[0].iov_len = locale->len[LC_TIME]; | |
437 | ||
438 | write_locale_data (output_path, "LC_TIME", 1, iov); | |
439 | ||
440 | return; | |
441 | } | |
442 | ||
443 | data.magic = LIMAGIC (LC_TIME); | |
444 | data.n = _NL_ITEM_INDEX (_NL_NUM_LC_TIME); | |
445 | iov[0].iov_base = (void *) &data; | |
446 | iov[0].iov_len = sizeof (data); | |
447 | ||
448 | iov[1].iov_base = (void *) idx; | |
449 | iov[1].iov_len = sizeof (idx); | |
450 | ||
451 | idx[0] = iov[0].iov_len + iov[1].iov_len; | |
452 | ||
453 | /* The ab'days. */ | |
454 | for (cnt = 0; cnt <= _NL_ITEM_INDEX (ABDAY_7); ++cnt) | |
455 | { | |
456 | iov[2 + cnt].iov_base = | |
457 | (void *) (time->abday[cnt - _NL_ITEM_INDEX (ABDAY_1)] ?: ""); | |
458 | iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1; | |
459 | idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len; | |
460 | } | |
461 | ||
462 | /* The days. */ | |
463 | for (; cnt <= _NL_ITEM_INDEX (DAY_7); ++cnt) | |
464 | { | |
465 | iov[2 + cnt].iov_base = | |
466 | (void *) (time->day[cnt - _NL_ITEM_INDEX (DAY_1)] ?: ""); | |
467 | iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1; | |
468 | idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len; | |
469 | } | |
470 | ||
471 | /* The ab'mons. */ | |
472 | for (; cnt <= _NL_ITEM_INDEX (ABMON_12); ++cnt) | |
473 | { | |
474 | iov[2 + cnt].iov_base = | |
475 | (void *) (time->abmon[cnt - _NL_ITEM_INDEX (ABMON_1)] ?: ""); | |
476 | iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1; | |
477 | idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len; | |
478 | } | |
479 | ||
480 | /* The mons. */ | |
481 | for (; cnt <= _NL_ITEM_INDEX (MON_12); ++cnt) | |
482 | { | |
483 | iov[2 + cnt].iov_base = | |
484 | (void *) (time->mon[cnt - _NL_ITEM_INDEX (MON_1)] ?: ""); | |
485 | iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1; | |
486 | idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len; | |
487 | } | |
488 | ||
489 | /* AM/PM. */ | |
490 | for (; cnt <= _NL_ITEM_INDEX (PM_STR); ++cnt) | |
491 | { | |
492 | iov[2 + cnt].iov_base = | |
493 | (void *) (time->am_pm[cnt - _NL_ITEM_INDEX (AM_STR)] ?: ""); | |
494 | iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1; | |
495 | idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len; | |
496 | } | |
497 | ||
498 | iov[2 + cnt].iov_base = (void *) (time->d_t_fmt ?: ""); | |
499 | iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1; | |
500 | idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len; | |
501 | ++cnt; | |
502 | ||
503 | iov[2 + cnt].iov_base = (void *) (time->d_fmt ?: ""); | |
504 | iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1; | |
505 | idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len; | |
506 | ++cnt; | |
507 | ||
508 | iov[2 + cnt].iov_base = (void *) (time->t_fmt ?: ""); | |
509 | iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1; | |
510 | idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len; | |
511 | ++cnt; | |
512 | ||
513 | iov[2 + cnt].iov_base = (void *) (time->t_fmt_ampm ?: ""); | |
514 | iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1; | |
515 | idx[1 + cnt] = idx[cnt] + iov[2 + cnt].iov_len; | |
ec4b0518 | 516 | last_idx = ++cnt; |
19bc17a9 | 517 | |
ec4b0518 | 518 | idx[1 + last_idx] = idx[last_idx]; |
c4029823 | 519 | for (num = 0; num < time->cur_num_era; ++num, ++cnt) |
ec4b0518 UD |
520 | { |
521 | iov[2 + cnt].iov_base = (void *) time->era[num]; | |
522 | iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1; | |
c4029823 | 523 | idx[1 + last_idx] += iov[2 + cnt].iov_len; |
ec4b0518 UD |
524 | } |
525 | ++last_idx; | |
19bc17a9 RM |
526 | |
527 | iov[2 + cnt].iov_base = (void *) (time->era_year ?: ""); | |
528 | iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1; | |
ec4b0518 | 529 | idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len; |
19bc17a9 | 530 | ++cnt; |
ec4b0518 | 531 | ++last_idx; |
19bc17a9 RM |
532 | |
533 | iov[2 + cnt].iov_base = (void *) (time->era_d_fmt ?: ""); | |
534 | iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1; | |
ec4b0518 UD |
535 | idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len; |
536 | ++cnt; | |
537 | ++last_idx; | |
19bc17a9 RM |
538 | |
539 | idx[1 + last_idx] = idx[last_idx]; | |
540 | for (num = 0; num < time->cur_num_alt_digits; ++num, ++cnt) | |
541 | { | |
542 | iov[2 + cnt].iov_base = (void *) (time->alt_digits[num] ?: ""); | |
543 | iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1; | |
544 | idx[1 + last_idx] += iov[2 + cnt].iov_len; | |
545 | } | |
546 | ++last_idx; | |
547 | ||
548 | iov[2 + cnt].iov_base = (void *) (time->era_d_t_fmt ?: ""); | |
549 | iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1; | |
550 | idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len; | |
551 | ++cnt; | |
c4029823 | 552 | ++last_idx; |
19bc17a9 | 553 | |
c4029823 | 554 | iov[2 + cnt].iov_base = (void *) (time->era_t_fmt ?: ""); |
19bc17a9 | 555 | iov[2 + cnt].iov_len = strlen (iov[2 + cnt].iov_base) + 1; |
c4029823 | 556 | idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len; |
19bc17a9 | 557 | ++cnt; |
c4029823 UD |
558 | ++last_idx; |
559 | ||
560 | ||
561 | /* We must align the following data. */ | |
562 | iov[2 + cnt].iov_base = (void *) "\0\0"; | |
563 | iov[2 + cnt].iov_len = ((idx[last_idx] + 3) & ~3) - idx[last_idx]; | |
564 | idx[last_idx] = (idx[last_idx] + 3) & ~3; | |
565 | ++cnt; | |
566 | ||
567 | iov[2 + cnt].iov_base = (void *) &time->cur_num_alt_digits; | |
568 | iov[2 + cnt].iov_len = sizeof (u_int32_t); | |
569 | idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len; | |
570 | ++cnt; | |
571 | ++last_idx; | |
572 | ||
573 | /* The `era' data in usable form. */ | |
574 | iov[2 + cnt].iov_base = (void *) &time->cur_num_era; | |
575 | iov[2 + cnt].iov_len = sizeof (u_int32_t); | |
576 | idx[1 + last_idx] = idx[last_idx] + iov[2 + cnt].iov_len; | |
577 | ++cnt; | |
578 | ++last_idx; | |
579 | ||
580 | #if __BYTE_ORDER == __LITTLE_ENDIAN | |
c4029823 UD |
581 | # define ERA_B1 time->era_entries_ob |
582 | # define ERA_B2 time->era_entries | |
d68171ed UD |
583 | #else |
584 | # define ERA_B1 time->era_entries | |
585 | # define ERA_B2 time->era_entries_ob | |
c4029823 UD |
586 | #endif |
587 | idx[1 + last_idx] = idx[last_idx]; | |
588 | for (num = 0; num < time->cur_num_era; ++num) | |
589 | { | |
590 | size_t l; | |
591 | ||
592 | iov[2 + cnt].iov_base = (void *) &ERA_B1[num].direction; | |
593 | iov[2 + cnt].iov_len = sizeof (int32_t); | |
594 | ++cnt; | |
595 | iov[2 + cnt].iov_base = (void *) &ERA_B1[num].offset; | |
596 | iov[2 + cnt].iov_len = sizeof (int32_t); | |
597 | ++cnt; | |
598 | iov[2 + cnt].iov_base = (void *) &ERA_B1[num].start_date[0]; | |
f76d7052 | 599 | iov[2 + cnt].iov_len = 3 * sizeof (int32_t); |
c4029823 UD |
600 | ++cnt; |
601 | iov[2 + cnt].iov_base = (void *) &ERA_B1[num].stop_date[0]; | |
f76d7052 | 602 | iov[2 + cnt].iov_len = 3 * sizeof (int32_t); |
c4029823 UD |
603 | ++cnt; |
604 | ||
605 | l = (strchr (ERA_B1[num].format, '\0') - ERA_B1[num].name) + 1; | |
606 | l = (l + 3) & ~3; | |
607 | iov[2 + cnt].iov_base = (void *) ERA_B1[num].name; | |
608 | iov[2 + cnt].iov_len = l; | |
609 | ++cnt; | |
610 | ||
611 | idx[1 + last_idx] += 8 * sizeof (int32_t) + l; | |
612 | ||
613 | assert (idx[1 + last_idx] % 4 == 0); | |
614 | } | |
615 | ++last_idx; | |
616 | ||
617 | /* idx[1 + last_idx] = idx[last_idx]; */ | |
618 | for (num = 0; num < time->cur_num_era; ++num) | |
619 | { | |
620 | size_t l; | |
621 | ||
622 | iov[2 + cnt].iov_base = (void *) &ERA_B2[num].direction; | |
623 | iov[2 + cnt].iov_len = sizeof (int32_t); | |
624 | ++cnt; | |
625 | iov[2 + cnt].iov_base = (void *) &ERA_B2[num].offset; | |
626 | iov[2 + cnt].iov_len = sizeof (int32_t); | |
627 | ++cnt; | |
628 | iov[2 + cnt].iov_base = (void *) &ERA_B2[num].start_date[0]; | |
f76d7052 | 629 | iov[2 + cnt].iov_len = 3 * sizeof (int32_t); |
c4029823 UD |
630 | ++cnt; |
631 | iov[2 + cnt].iov_base = (void *) &ERA_B2[num].stop_date[0]; | |
f76d7052 | 632 | iov[2 + cnt].iov_len = 3 * sizeof (int32_t); |
c4029823 UD |
633 | ++cnt; |
634 | ||
635 | l = (strchr (ERA_B2[num].format, '\0') - ERA_B2[num].name) + 1; | |
636 | l = (l + 3) & ~3; | |
637 | iov[2 + cnt].iov_base = (void *) ERA_B2[num].name; | |
638 | iov[2 + cnt].iov_len = l; | |
639 | ++cnt; | |
640 | ||
641 | /* idx[1 + last_idx] += 8 * sizeof (int32_t) + l; */ | |
642 | } | |
643 | ||
a68b0d31 UD |
644 | /* We have a problem when no era data is present. In this case the |
645 | data pointer for _NL_TIME_ERA_ENTRIES_EB and | |
646 | _NL_TIME_ERA_ENTRIES_EL point after the end of the file. So we | |
647 | introduce some dummy data here. */ | |
648 | if (time->cur_num_era == 0) | |
649 | { | |
650 | static u_int32_t dummy = 0; | |
651 | iov[2 + cnt].iov_base = (void *) &dummy; | |
652 | iov[2 + cnt].iov_len = 4; | |
653 | ++cnt; | |
654 | } | |
19bc17a9 | 655 | |
c4029823 UD |
656 | assert (cnt == (_NL_ITEM_INDEX (_NL_NUM_LC_TIME) |
657 | + time->cur_num_era - 1 | |
658 | + time->cur_num_alt_digits - 1 | |
a68b0d31 UD |
659 | + 1 + (time->cur_num_era * 9 - 1) * 2 |
660 | + (time->cur_num_era == 0)) | |
c4029823 | 661 | && last_idx + 1 == _NL_ITEM_INDEX (_NL_NUM_LC_TIME)); |
19bc17a9 RM |
662 | |
663 | write_locale_data (output_path, "LC_TIME", 2 + cnt, iov); | |
664 | } | |
665 | ||
666 | ||
667 | void | |
668 | time_add (struct linereader *lr, struct localedef_t *locale, | |
669 | enum token_t tok, struct token *code, | |
670 | struct charset_t *charset) | |
671 | { | |
672 | struct locale_time_t *time = locale->categories[LC_TIME].time; | |
673 | ||
674 | switch (tok) | |
675 | { | |
676 | #define STRARR_ELEM(cat, max) \ | |
677 | case tok_##cat: \ | |
678 | if (time->cur_num_##cat >= max) \ | |
036cc82f | 679 | lr_error (lr, _("\ |
ba1ffaa1 | 680 | too many values for field `%s' in category `%s'"), \ |
19bc17a9 RM |
681 | #cat, "LC_TIME"); \ |
682 | else if (code->val.str.start == NULL) \ | |
683 | { \ | |
684 | lr_error (lr, _("unknown character in field `%s' of category `%s'"),\ | |
685 | #cat, "LC_TIME"); \ | |
686 | time->cat[time->cur_num_##cat++] = ""; \ | |
687 | } \ | |
688 | else \ | |
ec4b0518 | 689 | time->cat[time->cur_num_##cat++] = code->val.str.start; \ |
19bc17a9 RM |
690 | break |
691 | ||
692 | STRARR_ELEM (abday, 7); | |
693 | STRARR_ELEM (day, 7); | |
694 | STRARR_ELEM (abmon, 12); | |
695 | STRARR_ELEM (mon, 12); | |
696 | STRARR_ELEM (am_pm, 2); | |
697 | STRARR_ELEM (alt_digits, 100); | |
698 | ||
ec4b0518 UD |
699 | case tok_era: |
700 | if (code->val.str.start == NULL) | |
701 | lr_error (lr, _("unknown character in field `%s' of category `%s'"), | |
702 | "era", "LC_TIME"); | |
703 | else | |
704 | { | |
c4029823 UD |
705 | ++time->cur_num_era; |
706 | time->era = xrealloc (time->era, | |
707 | time->cur_num_era * sizeof (char *)); | |
708 | time->era[time->cur_num_era - 1] = code->val.str.start; | |
ec4b0518 UD |
709 | } |
710 | break; | |
711 | ||
19bc17a9 RM |
712 | #define STR_ELEM(cat) \ |
713 | case tok_##cat: \ | |
714 | if (time->cat != NULL) \ | |
715 | lr_error (lr, _("\ | |
716 | field `%s' in category `%s' declared more than once"), \ | |
717 | #cat, "LC_TIME"); \ | |
718 | else if (code->val.str.start == NULL) \ | |
719 | { \ | |
720 | lr_error (lr, _("unknown character in field `%s' of category `%s'"),\ | |
721 | #cat, "LC_TIME"); \ | |
722 | time->cat = ""; \ | |
723 | } \ | |
724 | else \ | |
725 | time->cat = code->val.str.start; \ | |
726 | break | |
727 | ||
728 | STR_ELEM (d_t_fmt); | |
729 | STR_ELEM (d_fmt); | |
730 | STR_ELEM (t_fmt); | |
731 | STR_ELEM (t_fmt_ampm); | |
19bc17a9 RM |
732 | STR_ELEM (era_year); |
733 | STR_ELEM (era_d_t_fmt); | |
734 | STR_ELEM (era_d_fmt); | |
735 | STR_ELEM (era_t_fmt); | |
736 | ||
737 | default: | |
738 | assert (! "unknown token in category `LC_TIME': should not happen"); | |
739 | } | |
740 | } |