1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1985-2011 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Eclipse Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.eclipse.org/org/documents/epl-v10.html *
11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * Glenn Fowler <gsf@research.att.com> *
18 * David Korn <dgk@research.att.com> *
19 * Phong Vo <kpv@research.att.com> *
20 * *
21 ***********************************************************************/
22 #pragma prototyped
23 /*
24 * Glenn Fowler
25 * AT&T Research
26 *
27 * time conversion translation support
28 */
29
30 #include <ast.h>
31 #include <cdt.h>
32 #include <iconv.h>
33 #include <mc.h>
34 #include <tm.h>
35 #include <ast_nl_types.h>
36
37 #include "lclib.h"
38
39 static struct
40 {
41 char* format;
42 Lc_info_t* locale;
43 char null[1];
44 } state;
45
46 /*
47 * this is unix dadgummit
48 */
49
50 static int
standardized(Lc_info_t * li,register char ** b)51 standardized(Lc_info_t* li, register char** b)
52 {
53 if ((li->lc->language->flags & (LC_debug|LC_default)) || streq(li->lc->language->code, "en"))
54 {
55 b[TM_TIME] = "%H:%M:%S";
56 b[TM_DATE] = "%m/%d/%y";
57 b[TM_DEFAULT] = "%a %b %e %T %Z %Y";
58 return 1;
59 }
60 return 0;
61 }
62
63 /*
64 * fix up LC_TIME data after loading
65 */
66
67 static void
fixup(Lc_info_t * li,register char ** b)68 fixup(Lc_info_t* li, register char** b)
69 {
70 register char** v;
71 register char** e;
72 register int n;
73
74 static int must[] =
75 {
76 TM_TIME,
77 TM_DATE,
78 TM_DEFAULT,
79 TM_MERIDIAN,
80 TM_UT,
81 TM_DT,
82 TM_SUFFIXES,
83 TM_PARTS,
84 TM_HOURS,
85 TM_DAYS,
86 TM_LAST,
87 TM_THIS,
88 TM_NEXT,
89 TM_EXACT,
90 TM_NOISE,
91 TM_ORDINAL,
92 TM_CTIME,
93 TM_DATE_1,
94 TM_INTERNATIONAL,
95 TM_RECENT,
96 TM_DISTANT,
97 TM_MERIDIAN_TIME,
98 TM_ORDINALS,
99 TM_FINAL,
100 TM_WORK,
101 };
102
103 standardized(li, b);
104 for (v = b, e = b + TM_NFORM; v < e; v++)
105 if (!*v)
106 *v = state.null;
107 for (n = 0; n < elementsof(must); n++)
108 if (!*b[must[n]])
109 b[must[n]] = tm_data.format[must[n]];
110 if (li->lc->flags & LC_default)
111 for (n = 0; n < TM_NFORM; n++)
112 if (!*b[n])
113 b[n] = tm_data.format[n];
114 if (strchr(b[TM_UT], '%'))
115 {
116 tm_info.deformat = b[TM_UT];
117 for (n = TM_UT; n < TM_DT; n++)
118 b[n] = state.null;
119 }
120 else
121 tm_info.deformat = b[TM_DEFAULT];
122 tm_info.format = b;
123 if (!(tm_info.deformat = state.format))
124 tm_info.deformat = tm_info.format[TM_DEFAULT];
125 li->data = (void*)b;
126 }
127
128 #if _WINIX
129
130 #include <ast_windows.h>
131
132 typedef struct Map_s
133 {
134 LCID native;
135 int local;
136 } Map_t;
137
138 static const Map_t map[] =
139 {
140 LOCALE_S1159, (TM_MERIDIAN+0),
141 LOCALE_S2359, (TM_MERIDIAN+1),
142 LOCALE_SABBREVDAYNAME1, (TM_DAY_ABBREV+1),
143 LOCALE_SABBREVDAYNAME2, (TM_DAY_ABBREV+2),
144 LOCALE_SABBREVDAYNAME3, (TM_DAY_ABBREV+3),
145 LOCALE_SABBREVDAYNAME4, (TM_DAY_ABBREV+4),
146 LOCALE_SABBREVDAYNAME5, (TM_DAY_ABBREV+5),
147 LOCALE_SABBREVDAYNAME6, (TM_DAY_ABBREV+6),
148 LOCALE_SABBREVDAYNAME7, (TM_DAY_ABBREV+0),
149 LOCALE_SABBREVMONTHNAME1, (TM_MONTH_ABBREV+0),
150 LOCALE_SABBREVMONTHNAME2, (TM_MONTH_ABBREV+1),
151 LOCALE_SABBREVMONTHNAME3, (TM_MONTH_ABBREV+2),
152 LOCALE_SABBREVMONTHNAME4, (TM_MONTH_ABBREV+3),
153 LOCALE_SABBREVMONTHNAME5, (TM_MONTH_ABBREV+4),
154 LOCALE_SABBREVMONTHNAME6, (TM_MONTH_ABBREV+5),
155 LOCALE_SABBREVMONTHNAME7, (TM_MONTH_ABBREV+6),
156 LOCALE_SABBREVMONTHNAME8, (TM_MONTH_ABBREV+7),
157 LOCALE_SABBREVMONTHNAME9, (TM_MONTH_ABBREV+8),
158 LOCALE_SABBREVMONTHNAME10, (TM_MONTH_ABBREV+9),
159 LOCALE_SABBREVMONTHNAME11, (TM_MONTH_ABBREV+10),
160 LOCALE_SABBREVMONTHNAME12, (TM_MONTH_ABBREV+11),
161 LOCALE_SDAYNAME1, (TM_DAY+1),
162 LOCALE_SDAYNAME2, (TM_DAY+2),
163 LOCALE_SDAYNAME3, (TM_DAY+3),
164 LOCALE_SDAYNAME4, (TM_DAY+4),
165 LOCALE_SDAYNAME5, (TM_DAY+5),
166 LOCALE_SDAYNAME6, (TM_DAY+6),
167 LOCALE_SDAYNAME7, (TM_DAY+0),
168 LOCALE_SMONTHNAME1, (TM_MONTH+0),
169 LOCALE_SMONTHNAME2, (TM_MONTH+1),
170 LOCALE_SMONTHNAME3, (TM_MONTH+2),
171 LOCALE_SMONTHNAME4, (TM_MONTH+3),
172 LOCALE_SMONTHNAME5, (TM_MONTH+4),
173 LOCALE_SMONTHNAME6, (TM_MONTH+5),
174 LOCALE_SMONTHNAME7, (TM_MONTH+6),
175 LOCALE_SMONTHNAME8, (TM_MONTH+7),
176 LOCALE_SMONTHNAME9, (TM_MONTH+8),
177 LOCALE_SMONTHNAME10, (TM_MONTH+9),
178 LOCALE_SMONTHNAME11, (TM_MONTH+10),
179 LOCALE_SMONTHNAME12, (TM_MONTH+11),
180 };
181
182 #undef extern
183
184 /*
185 * convert ms word date spec w to posix strftime format f
186 * next char after f returned
187 * the caller already made sure f is big enough
188 */
189
190 static char*
word2posix(register char * f,register char * w,int alternate)191 word2posix(register char* f, register char* w, int alternate)
192 {
193 register char* r;
194 register int c;
195 register int p;
196 register int n;
197
198 while (*w)
199 {
200 p = 0;
201 r = w;
202 while (*++w == *r);
203 if ((n = w - r) > 3 && alternate)
204 n--;
205 switch (*r)
206 {
207 case 'a':
208 case 'A':
209 if (!strncasecmp(w, "am/pm", 5))
210 w += 5;
211 else if (!strncasecmp(w, "a/p", 3))
212 w += 3;
213 c = 'p';
214 break;
215 case 'd':
216 switch (n)
217 {
218 case 1:
219 p = '-';
220 /*FALLTHROUGH*/
221 case 2:
222 c = 'd';
223 break;
224 case 3:
225 c = 'a';
226 break;
227 default:
228 c = 'A';
229 break;
230 }
231 break;
232 case 'h':
233 switch (n)
234 {
235 case 1:
236 p = '-';
237 /*FALLTHROUGH*/
238 default:
239 c = 'I';
240 break;
241 }
242 break;
243 case 'H':
244 switch (n)
245 {
246 case 1:
247 p = '-';
248 /*FALLTHROUGH*/
249 default:
250 c = 'H';
251 break;
252 }
253 break;
254 case 'M':
255 switch (n)
256 {
257 case 1:
258 p = '-';
259 /*FALLTHROUGH*/
260 case 2:
261 c = 'm';
262 break;
263 case 3:
264 c = 'b';
265 break;
266 default:
267 c = 'B';
268 break;
269 }
270 break;
271 case 'm':
272 switch (n)
273 {
274 case 1:
275 p = '-';
276 /*FALLTHROUGH*/
277 default:
278 c = 'M';
279 break;
280 }
281 break;
282 case 's':
283 switch (n)
284 {
285 case 1:
286 p = '-';
287 /*FALLTHROUGH*/
288 default:
289 c = 'S';
290 break;
291 }
292 break;
293 case 'y':
294 switch (n)
295 {
296 case 1:
297 p = '-';
298 /*FALLTHROUGH*/
299 case 2:
300 c = 'y';
301 break;
302 default:
303 c = 'Y';
304 break;
305 }
306 break;
307 case '\'':
308 if (n & 1)
309 for (w = r + 1; *w; *f++ = *w++)
310 if (*w == '\'')
311 {
312 w++;
313 break;
314 }
315 continue;
316 case '%':
317 while (r < w)
318 {
319 *f++ = *r++;
320 *f++ = *r++;
321 }
322 continue;
323 default:
324 while (r < w)
325 *f++ = *r++;
326 continue;
327 }
328 *f++ = '%';
329 if (p)
330 *f++ = '-';
331 *f++ = c;
332 }
333 *f++ = 0;
334 return f;
335 }
336
337 /*
338 * load the native LC_TIME data for the current locale
339 */
340
341 static void
native_lc_time(Lc_info_t * li)342 native_lc_time(Lc_info_t* li)
343 {
344 register char* s;
345 register char* t;
346 register char** b;
347 register int n;
348 register int m;
349 register int i;
350 LCID lcid;
351 int nt;
352 int ns;
353 int nl;
354 int clock_24;
355 int leading_0;
356 char buf[256];
357
358 lcid = li->lc->index;
359 nt = 2 * GetLocaleInfo(lcid, LOCALE_STIME, 0, 0) + 7; /* HH:MM:SS */
360 ns = 3 * GetLocaleInfo(lcid, LOCALE_SSHORTDATE, 0, 0);
361 nl = 3 * GetLocaleInfo(lcid, LOCALE_SLONGDATE, 0, 0);
362 n = nt + ns + nl;
363 for (i = 0; i < elementsof(map); i++)
364 n += GetLocaleInfo(lcid, map[i].native, 0, 0);
365 if (!(b = newof(0, char*, TM_NFORM, n)))
366 return;
367 s = (char*)(b + TM_NFORM);
368 for (i = 0; i < elementsof(map); i++)
369 {
370 if (!(m = GetLocaleInfo(lcid, map[i].native, s, n)))
371 goto bad;
372 b[map[i].local] = s;
373 s += m;
374 }
375 if (!standardized(li, b))
376 {
377 /*
378 * synthesize TM_TIME format from the ms word template
379 */
380
381 if (!GetLocaleInfo(lcid, LOCALE_ITIME, buf, sizeof(buf)))
382 goto bad;
383 clock_24 = atoi(buf);
384 if (!GetLocaleInfo(lcid, LOCALE_ITLZERO, buf, sizeof(buf)))
385 goto bad;
386 leading_0 = atoi(buf);
387 if (!GetLocaleInfo(lcid, LOCALE_STIME, buf, sizeof(buf)))
388 goto bad;
389 b[TM_TIME] = s;
390 *s++ = '%';
391 if (!leading_0)
392 *s++ = '-';
393 *s++ = clock_24 ? 'H' : 'I';
394 for (t = buf; *s = *t++; s++);
395 *s++ = '%';
396 if (!leading_0)
397 *s++ = '-';
398 *s++ = 'M';
399 for (t = buf; *s = *t++; s++);
400 *s++ = '%';
401 if (!leading_0)
402 *s++ = '-';
403 *s++ = 'S';
404 *s++ = 0;
405
406 /*
407 * synthesize TM_DATE format
408 */
409
410 if (!GetLocaleInfo(lcid, LOCALE_SSHORTDATE, buf, sizeof(buf)))
411 goto bad;
412 b[TM_DATE] = s;
413 s = word2posix(s, buf, 1);
414
415 /*
416 * synthesize TM_DEFAULT format
417 */
418
419 if (!GetLocaleInfo(lcid, LOCALE_SLONGDATE, buf, sizeof(buf)))
420 goto bad;
421 b[TM_DEFAULT] = s;
422 s = word2posix(s, buf, 1);
423 strcpy(s - 1, " %X");
424 }
425
426 /*
427 * done
428 */
429
430 fixup(li, b);
431 return;
432 bad:
433 free(b);
434 }
435
436 #else
437
438 #if _lib_nl_langinfo && _hdr_langinfo
439
440 #if _hdr_nl_types
441 #include <nl_types.h>
442 #endif
443
444 #include <langinfo.h>
445
446 typedef struct Map_s
447 {
448 int native;
449 int local;
450 } Map_t;
451
452 static const Map_t map[] =
453 {
454 AM_STR, (TM_MERIDIAN+0),
455 PM_STR, (TM_MERIDIAN+1),
456 ABDAY_1, (TM_DAY_ABBREV+0),
457 ABDAY_2, (TM_DAY_ABBREV+1),
458 ABDAY_3, (TM_DAY_ABBREV+2),
459 ABDAY_4, (TM_DAY_ABBREV+3),
460 ABDAY_5, (TM_DAY_ABBREV+4),
461 ABDAY_6, (TM_DAY_ABBREV+5),
462 ABDAY_7, (TM_DAY_ABBREV+6),
463 ABMON_1, (TM_MONTH_ABBREV+0),
464 ABMON_2, (TM_MONTH_ABBREV+1),
465 ABMON_3, (TM_MONTH_ABBREV+2),
466 ABMON_4, (TM_MONTH_ABBREV+3),
467 ABMON_5, (TM_MONTH_ABBREV+4),
468 ABMON_6, (TM_MONTH_ABBREV+5),
469 ABMON_7, (TM_MONTH_ABBREV+6),
470 ABMON_8, (TM_MONTH_ABBREV+7),
471 ABMON_9, (TM_MONTH_ABBREV+8),
472 ABMON_10, (TM_MONTH_ABBREV+9),
473 ABMON_11, (TM_MONTH_ABBREV+10),
474 ABMON_12, (TM_MONTH_ABBREV+11),
475 DAY_1, (TM_DAY+0),
476 DAY_2, (TM_DAY+1),
477 DAY_3, (TM_DAY+2),
478 DAY_4, (TM_DAY+3),
479 DAY_5, (TM_DAY+4),
480 DAY_6, (TM_DAY+5),
481 DAY_7, (TM_DAY+6),
482 MON_1, (TM_MONTH+0),
483 MON_2, (TM_MONTH+1),
484 MON_3, (TM_MONTH+2),
485 MON_4, (TM_MONTH+3),
486 MON_5, (TM_MONTH+4),
487 MON_6, (TM_MONTH+5),
488 MON_7, (TM_MONTH+6),
489 MON_8, (TM_MONTH+7),
490 MON_9, (TM_MONTH+8),
491 MON_10, (TM_MONTH+9),
492 MON_11, (TM_MONTH+10),
493 MON_12, (TM_MONTH+11),
494 #ifdef _DATE_FMT
495 _DATE_FMT, TM_DEFAULT,
496 #else
497 D_T_FMT, TM_DEFAULT,
498 #endif
499 D_FMT, TM_DATE,
500 T_FMT, TM_TIME,
501 #ifdef ERA
502 ERA, TM_ERA,
503 ERA_D_T_FMT, TM_ERA_DEFAULT,
504 ERA_D_FMT, TM_ERA_DATE,
505 ERA_T_FMT, TM_ERA_TIME,
506 #endif
507 #ifdef ALT_DIGITS
508 ALT_DIGITS, TM_DIGITS,
509 #endif
510 };
511
512 static void
native_lc_time(Lc_info_t * li)513 native_lc_time(Lc_info_t* li)
514 {
515 register char* s;
516 register char* t;
517 register char** b;
518 register int n;
519 register int i;
520
521 n = 0;
522 for (i = 0; i < elementsof(map); i++)
523 {
524 if (!(t = nl_langinfo(map[i].native)))
525 t = tm_data.format[map[i].local];
526 n += strlen(t) + 1;
527 }
528 if (!(b = newof(0, char*, TM_NFORM, n)))
529 return;
530 s = (char*)(b + TM_NFORM);
531 for (i = 0; i < elementsof(map); i++)
532 {
533 b[map[i].local] = s;
534 if (!(t = nl_langinfo(map[i].native)))
535 t = tm_data.format[map[i].local];
536 while (*s++ = *t++);
537 }
538 fixup(li, b);
539 }
540
541 #else
542
543 #define native_lc_time(li) ((li->data=(void*)(tm_info.format=tm_data.format)),(tm_info.deformat=tm_info.format[TM_DEFAULT]))
544
545 #endif
546
547 #endif
548
549 /*
550 * load the LC_TIME data for the current locale
551 */
552
553 static void
load(Lc_info_t * li)554 load(Lc_info_t* li)
555 {
556 register char* s;
557 register char** b;
558 register char** v;
559 register char** e;
560 unsigned char* u;
561 ssize_t n;
562 iconv_t cvt;
563 Sfio_t* sp;
564 Sfio_t* tp;
565 char path[PATH_MAX];
566
567 if (b = (char**)li->data)
568 {
569 tm_info.format = b;
570 if (!(tm_info.deformat = state.format))
571 tm_info.deformat = tm_info.format[TM_DEFAULT];
572 return;
573 }
574 tm_info.format = tm_data.format;
575 if (!(tm_info.deformat = state.format))
576 tm_info.deformat = tm_info.format[TM_DEFAULT];
577 if (mcfind(NiL, NiL, LC_TIME, 0, path, sizeof(path)) && (sp = sfopen(NiL, path, "r")))
578 {
579 n = sfsize(sp);
580 tp = 0;
581 if (u = (unsigned char*)sfreserve(sp, 3, 1))
582 {
583 if (u[0] == 0xef && u[1] == 0xbb && u[2] == 0xbf && (cvt = iconv_open("", "utf")) != (iconv_t)(-1))
584 {
585 if (tp = sfstropen())
586 {
587 sfread(sp, u, 3);
588 n = iconv_move(cvt, sp, tp, SF_UNBOUND, NiL);
589 }
590 iconv_close(cvt);
591 }
592 if (!tp)
593 sfread(sp, u, 0);
594 }
595 if (b = newof(0, char*, TM_NFORM, n + 2))
596 {
597 v = b;
598 e = b + TM_NFORM;
599 s = (char*)e;
600 if (tp && memcpy(s, sfstrbase(tp), n) || !tp && sfread(sp, s, n) == n)
601 {
602 s[n] = '\n';
603 while (v < e)
604 {
605 *v++ = s;
606 if (!(s = strchr(s, '\n')))
607 break;
608 *s++ = 0;
609 }
610 fixup(li, b);
611 }
612 else
613 free(b);
614 }
615 if (tp)
616 sfclose(tp);
617 sfclose(sp);
618 }
619 else
620 native_lc_time(li);
621 }
622
623 /*
624 * check that tm_info.format matches the current locale
625 */
626
627 char**
tmlocale(void)628 tmlocale(void)
629 {
630 Lc_info_t* li;
631
632 if (!tm_info.format)
633 {
634 tm_info.format = tm_data.format;
635 if (!tm_info.deformat)
636 tm_info.deformat = tm_info.format[TM_DEFAULT];
637 else if (tm_info.deformat != tm_info.format[TM_DEFAULT])
638 state.format = tm_info.deformat;
639 }
640 li = LCINFO(AST_LC_TIME);
641 if (!li->data)
642 load(li);
643 return tm_info.format;
644 }
645