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