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