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 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 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* 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 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 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 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** 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