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