1 /* $NetBSD: strftime.c,v 1.20 2009/12/31 22:49:16 mlelstv Exp $ */ 2 3 #include <sys/cdefs.h> 4 #if defined(LIBC_SCCS) && !defined(lint) 5 #if 0 6 static char elsieid[] = "@(#)strftime.c 7.64"; 7 static char elsieid[] = "@(#)strftime.c 8.3"; 8 #else 9 __RCSID("$NetBSD: strftime.c,v 1.20 2009/12/31 22:49:16 mlelstv Exp $"); 10 #endif 11 #endif /* LIBC_SCCS and not lint */ 12 13 #include "namespace.h" 14 15 /* 16 ** Based on the UCB version with the ID appearing below. 17 ** This is ANSIish only when "multibyte character == plain character". 18 */ 19 20 #include "private.h" 21 22 /* 23 ** We don't use these extensions in strftime operation even when 24 ** supported by the local tzcode configuration. A strictly 25 ** conforming C application may leave them in undefined state. 26 */ 27 28 #ifdef _LIBC 29 #undef TM_ZONE 30 #undef TM_GMTOFF 31 #endif 32 33 /* 34 ** Copyright (c) 1989, 1993 35 ** The Regents of the University of California. All rights reserved. 36 ** 37 ** Redistribution and use in source and binary forms, with or without 38 ** modification, are permitted provided that the following conditions 39 ** are met: 40 ** 1. Redistributions of source code must retain the above copyright 41 ** notice, this list of conditions and the following disclaimer. 42 ** 2. Redistributions in binary form must reproduce the above copyright 43 ** notice, this list of conditions and the following disclaimer in the 44 ** documentation and/or other materials provided with the distribution. 45 ** 3. All advertising materials mentioning features or use of this software 46 ** must display the following acknowledgement: 47 ** This product includes software developed by the University of 48 ** California, Berkeley and its contributors. 49 ** 4. Neither the name of the University nor the names of its contributors 50 ** may be used to endorse or promote products derived from this software 51 ** without specific prior written permission. 52 ** 53 ** THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 54 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 55 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 56 ** ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 57 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 58 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 59 ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 60 ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 61 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 62 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 63 ** SUCH DAMAGE. 64 */ 65 66 #ifndef LIBC_SCCS 67 #ifndef lint 68 static const char sccsid[] = "@(#)strftime.c 5.4 (Berkeley) 3/14/89"; 69 #endif /* !defined lint */ 70 #endif /* !defined LIBC_SCCS */ 71 72 #include "tzfile.h" 73 #include "fcntl.h" 74 #include "locale.h" 75 76 #include "sys/localedef.h" 77 #define Locale _CurrentTimeLocale 78 #define c_fmt d_t_fmt 79 80 static char * _add(const char *, char *, const char *); 81 static char * _conv(int, const char *, char *, const char *); 82 static char * _fmt(const char *, const struct tm *, char *, const char *, 83 int *); 84 static char * _yconv(int, int, int, int, char *, const char *); 85 86 extern char * tzname[]; 87 88 #ifndef YEAR_2000_NAME 89 #define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS" 90 #endif /* !defined YEAR_2000_NAME */ 91 92 #define IN_NONE 0 93 #define IN_SOME 1 94 #define IN_THIS 2 95 #define IN_ALL 3 96 97 size_t 98 strftime(s, maxsize, format, t) 99 char * const s; 100 const size_t maxsize; 101 const char * const format; 102 const struct tm * const t; 103 { 104 char * p; 105 int warn; 106 107 tzset(); 108 warn = IN_NONE; 109 p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn); 110 #ifndef NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU 111 if (warn != IN_NONE && getenv(YEAR_2000_NAME) != NULL) { 112 (void) fprintf(stderr, "\n"); 113 if (format == NULL) 114 (void) fprintf(stderr, "NULL strftime format "); 115 else (void) fprintf(stderr, "strftime format \"%s\" ", 116 format); 117 (void) fprintf(stderr, "yields only two digits of years in "); 118 if (warn == IN_SOME) 119 (void) fprintf(stderr, "some locales"); 120 else if (warn == IN_THIS) 121 (void) fprintf(stderr, "the current locale"); 122 else (void) fprintf(stderr, "all locales"); 123 (void) fprintf(stderr, "\n"); 124 } 125 #endif /* !defined NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU */ 126 if (p == s + maxsize) 127 return 0; 128 *p = '\0'; 129 return p - s; 130 } 131 132 static char * 133 _fmt(format, t, pt, ptlim, warnp) 134 const char * format; 135 const struct tm * const t; 136 char * pt; 137 const char * const ptlim; 138 int * warnp; 139 { 140 for ( ; *format; ++format) { 141 if (*format == '%') { 142 label: 143 switch (*++format) { 144 case '\0': 145 --format; 146 break; 147 case 'A': 148 pt = _add((t->tm_wday < 0 || 149 t->tm_wday >= DAYSPERWEEK) ? 150 "?" : Locale->day[t->tm_wday], 151 pt, ptlim); 152 continue; 153 case 'a': 154 pt = _add((t->tm_wday < 0 || 155 t->tm_wday >= DAYSPERWEEK) ? 156 "?" : Locale->abday[t->tm_wday], 157 pt, ptlim); 158 continue; 159 case 'B': 160 pt = _add((t->tm_mon < 0 || 161 t->tm_mon >= MONSPERYEAR) ? 162 "?" : Locale->mon[t->tm_mon], 163 pt, ptlim); 164 continue; 165 case 'b': 166 case 'h': 167 pt = _add((t->tm_mon < 0 || 168 t->tm_mon >= MONSPERYEAR) ? 169 "?" : Locale->abmon[t->tm_mon], 170 pt, ptlim); 171 continue; 172 case 'C': 173 /* 174 ** %C used to do a... 175 ** _fmt("%a %b %e %X %Y", t); 176 ** ...whereas now POSIX 1003.2 calls for 177 ** something completely different. 178 ** (ado, 1993-05-24) 179 */ 180 pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 0, 181 pt, ptlim); 182 continue; 183 case 'c': 184 { 185 int warn2 = IN_SOME; 186 187 pt = _fmt(Locale->c_fmt, t, pt, ptlim, &warn2); 188 if (warn2 == IN_ALL) 189 warn2 = IN_THIS; 190 if (warn2 > *warnp) 191 *warnp = warn2; 192 } 193 continue; 194 case 'D': 195 pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp); 196 continue; 197 case 'd': 198 pt = _conv(t->tm_mday, "%02d", pt, ptlim); 199 continue; 200 case 'E': 201 case 'O': 202 /* 203 ** C99 locale modifiers. 204 ** The sequences 205 ** %Ec %EC %Ex %EX %Ey %EY 206 ** %Od %oe %OH %OI %Om %OM 207 ** %OS %Ou %OU %OV %Ow %OW %Oy 208 ** are supposed to provide alternate 209 ** representations. 210 */ 211 goto label; 212 case 'e': 213 pt = _conv(t->tm_mday, "%2d", pt, ptlim); 214 continue; 215 case 'F': 216 pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp); 217 continue; 218 case 'H': 219 pt = _conv(t->tm_hour, "%02d", pt, ptlim); 220 continue; 221 case 'I': 222 pt = _conv((t->tm_hour % 12) ? 223 (t->tm_hour % 12) : 12, 224 "%02d", pt, ptlim); 225 continue; 226 case 'j': 227 pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim); 228 continue; 229 case 'k': 230 /* 231 ** This used to be... 232 ** _conv(t->tm_hour % 12 ? 233 ** t->tm_hour % 12 : 12, 2, ' '); 234 ** ...and has been changed to the below to 235 ** match SunOS 4.1.1 and Arnold Robbins' 236 ** strftime version 3.0. That is, "%k" and 237 ** "%l" have been swapped. 238 ** (ado, 1993-05-24) 239 */ 240 pt = _conv(t->tm_hour, "%2d", pt, ptlim); 241 continue; 242 #ifdef KITCHEN_SINK 243 case 'K': 244 /* 245 ** After all this time, still unclaimed! 246 */ 247 pt = _add("kitchen sink", pt, ptlim); 248 continue; 249 #endif /* defined KITCHEN_SINK */ 250 case 'l': 251 /* 252 ** This used to be... 253 ** _conv(t->tm_hour, 2, ' '); 254 ** ...and has been changed to the below to 255 ** match SunOS 4.1.1 and Arnold Robbin's 256 ** strftime version 3.0. That is, "%k" and 257 ** "%l" have been swapped. 258 ** (ado, 1993-05-24) 259 */ 260 pt = _conv((t->tm_hour % 12) ? 261 (t->tm_hour % 12) : 12, 262 "%2d", pt, ptlim); 263 continue; 264 case 'M': 265 pt = _conv(t->tm_min, "%02d", pt, ptlim); 266 continue; 267 case 'm': 268 pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim); 269 continue; 270 case 'n': 271 pt = _add("\n", pt, ptlim); 272 continue; 273 case 'p': 274 pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ? 275 Locale->am_pm[1] : 276 Locale->am_pm[0], 277 pt, ptlim); 278 continue; 279 case 'R': 280 pt = _fmt("%H:%M", t, pt, ptlim, warnp); 281 continue; 282 case 'r': 283 pt = _fmt(Locale->t_fmt_ampm, t, pt, ptlim, 284 warnp); 285 continue; 286 case 'S': 287 pt = _conv(t->tm_sec, "%02d", pt, ptlim); 288 continue; 289 case 's': 290 { 291 struct tm tm; 292 char buf[INT_STRLEN_MAXIMUM( 293 time_t) + 1]; 294 time_t mkt; 295 296 tm = *t; 297 mkt = mktime(&tm); 298 /* CONSTCOND */ 299 if (TYPE_SIGNED(time_t)) 300 (void) snprintf(buf, sizeof(buf), 301 "%lld", (long long) mkt); 302 else (void) snprintf(buf, sizeof(buf), 303 "%llu", (unsigned long long) 304 mkt); 305 pt = _add(buf, pt, ptlim); 306 } 307 continue; 308 case 'T': 309 pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp); 310 continue; 311 case 't': 312 pt = _add("\t", pt, ptlim); 313 continue; 314 case 'U': 315 pt = _conv((t->tm_yday + DAYSPERWEEK - 316 t->tm_wday) / DAYSPERWEEK, 317 "%02d", pt, ptlim); 318 continue; 319 case 'u': 320 /* 321 ** From Arnold Robbins' strftime version 3.0: 322 ** "ISO 8601: Weekday as a decimal number 323 ** [1 (Monday) - 7]" 324 ** (ado, 1993-05-24) 325 */ 326 pt = _conv((t->tm_wday == 0) ? 327 DAYSPERWEEK : t->tm_wday, 328 "%d", pt, ptlim); 329 continue; 330 case 'V': /* ISO 8601 week number */ 331 case 'G': /* ISO 8601 year (four digits) */ 332 case 'g': /* ISO 8601 year (two digits) */ 333 /* 334 ** From Arnold Robbins' strftime version 3.0: "the week number of the 335 ** year (the first Monday as the first day of week 1) as a decimal number 336 ** (01-53)." 337 ** (ado, 1993-05-24) 338 ** 339 ** From "http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html" by Markus Kuhn: 340 ** "Week 01 of a year is per definition the first week which has the 341 ** Thursday in this year, which is equivalent to the week which contains 342 ** the fourth day of January. In other words, the first week of a new year 343 ** is the week which has the majority of its days in the new year. Week 01 344 ** might also contain days from the previous year and the week before week 345 ** 01 of a year is the last week (52 or 53) of the previous year even if 346 ** it contains days from the new year. A week starts with Monday (day 1) 347 ** and ends with Sunday (day 7). For example, the first week of the year 348 ** 1997 lasts from 1996-12-30 to 1997-01-05..." 349 ** (ado, 1996-01-02) 350 */ 351 { 352 int year; 353 int base; 354 int yday; 355 int wday; 356 int w; 357 358 year = t->tm_year; 359 base = TM_YEAR_BASE; 360 yday = t->tm_yday; 361 wday = t->tm_wday; 362 for ( ; ; ) { 363 int len; 364 int bot; 365 int top; 366 367 len = isleap_sum(year, base) ? 368 DAYSPERLYEAR : 369 DAYSPERNYEAR; 370 /* 371 ** What yday (-3 ... 3) does 372 ** the ISO year begin on? 373 */ 374 bot = ((yday + 11 - wday) % 375 DAYSPERWEEK) - 3; 376 /* 377 ** What yday does the NEXT 378 ** ISO year begin on? 379 */ 380 top = bot - 381 (len % DAYSPERWEEK); 382 if (top < -3) 383 top += DAYSPERWEEK; 384 top += len; 385 if (yday >= top) { 386 ++base; 387 w = 1; 388 break; 389 } 390 if (yday >= bot) { 391 w = 1 + ((yday - bot) / 392 DAYSPERWEEK); 393 break; 394 } 395 --base; 396 yday += isleap_sum(year, base) ? 397 DAYSPERLYEAR : 398 DAYSPERNYEAR; 399 } 400 #ifdef XPG4_1994_04_09 401 if ((w == 52 && 402 t->tm_mon == TM_JANUARY) || 403 (w == 1 && 404 t->tm_mon == TM_DECEMBER)) 405 w = 53; 406 #endif /* defined XPG4_1994_04_09 */ 407 if (*format == 'V') 408 pt = _conv(w, "%02d", 409 pt, ptlim); 410 else if (*format == 'g') { 411 *warnp = IN_ALL; 412 pt = _yconv(year, base, 0, 1, 413 pt, ptlim); 414 } else pt = _yconv(year, base, 1, 1, 415 pt, ptlim); 416 } 417 continue; 418 case 'v': 419 /* 420 ** From Arnold Robbins' strftime version 3.0: 421 ** "date as dd-bbb-YYYY" 422 ** (ado, 1993-05-24) 423 */ 424 pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp); 425 continue; 426 case 'W': 427 pt = _conv((t->tm_yday + DAYSPERWEEK - 428 (t->tm_wday ? 429 (t->tm_wday - 1) : 430 (DAYSPERWEEK - 1))) / DAYSPERWEEK, 431 "%02d", pt, ptlim); 432 continue; 433 case 'w': 434 pt = _conv(t->tm_wday, "%d", pt, ptlim); 435 continue; 436 case 'X': 437 pt = _fmt(Locale->t_fmt, t, pt, ptlim, warnp); 438 continue; 439 case 'x': 440 { 441 int warn2 = IN_SOME; 442 443 pt = _fmt(Locale->d_fmt, t, pt, ptlim, &warn2); 444 if (warn2 == IN_ALL) 445 warn2 = IN_THIS; 446 if (warn2 > *warnp) 447 *warnp = warn2; 448 } 449 continue; 450 case 'y': 451 *warnp = IN_ALL; 452 pt = _yconv(t->tm_year, TM_YEAR_BASE, 0, 1, 453 pt, ptlim); 454 continue; 455 case 'Y': 456 pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 1, 457 pt, ptlim); 458 continue; 459 case 'Z': 460 #ifdef TM_ZONE 461 if (t->TM_ZONE != NULL) 462 pt = _add(t->TM_ZONE, pt, ptlim); 463 else 464 #endif /* defined TM_ZONE */ 465 if (t->tm_isdst >= 0) 466 pt = _add(tzname[t->tm_isdst != 0], 467 pt, ptlim); 468 /* 469 ** C99 says that %Z must be replaced by the 470 ** empty string if the time zone is not 471 ** determinable. 472 */ 473 continue; 474 case 'z': 475 { 476 int diff; 477 char const * sign; 478 479 if (t->tm_isdst < 0) 480 continue; 481 #ifdef TM_GMTOFF 482 diff = (int)t->TM_GMTOFF; 483 #else /* !defined TM_GMTOFF */ 484 /* 485 ** C99 says that the UTC offset must 486 ** be computed by looking only at 487 ** tm_isdst. This requirement is 488 ** incorrect, since it means the code 489 ** must rely on magic (in this case 490 ** altzone and timezone), and the 491 ** magic might not have the correct 492 ** offset. Doing things correctly is 493 ** tricky and requires disobeying C99; 494 ** see GNU C strftime for details. 495 ** For now, punt and conform to the 496 ** standard, even though it's incorrect. 497 ** 498 ** C99 says that %z must be replaced by the 499 ** empty string if the time zone is not 500 ** determinable, so output nothing if the 501 ** appropriate variables are not available. 502 */ 503 #ifndef STD_INSPIRED 504 if (t->tm_isdst == 0) 505 #ifdef USG_COMPAT 506 diff = -timezone; 507 #else /* !defined USG_COMPAT */ 508 continue; 509 #endif /* !defined USG_COMPAT */ 510 else 511 #ifdef ALTZONE 512 diff = -altzone; 513 #else /* !defined ALTZONE */ 514 continue; 515 #endif /* !defined ALTZONE */ 516 #else /* defined STD_INSPIRED */ 517 { 518 struct tm tmp; 519 time_t lct, gct; 520 521 /* 522 ** Get calendar time from t 523 ** being treated as local. 524 */ 525 tmp = *t; /* mktime discards const */ 526 lct = mktime(&tmp); 527 528 if (lct == (time_t)-1) 529 continue; 530 531 /* 532 ** Get calendar time from t 533 ** being treated as GMT. 534 **/ 535 tmp = *t; /* mktime discards const */ 536 gct = timegm(&tmp); 537 538 if (gct == (time_t)-1) 539 continue; 540 541 /* LINTED difference will fit int */ 542 diff = (intmax_t)gct - (intmax_t)lct; 543 } 544 #endif /* defined STD_INSPIRED */ 545 #endif /* !defined TM_GMTOFF */ 546 if (diff < 0) { 547 sign = "-"; 548 diff = -diff; 549 } else sign = "+"; 550 pt = _add(sign, pt, ptlim); 551 diff /= SECSPERMIN; 552 diff = (diff / MINSPERHOUR) * 100 + 553 (diff % MINSPERHOUR); 554 pt = _conv(diff, "%04d", pt, ptlim); 555 } 556 continue; 557 #if 0 558 case '+': 559 pt = _fmt(Locale->date_fmt, t, pt, ptlim, 560 warnp); 561 continue; 562 #endif 563 case '%': 564 /* 565 ** X311J/88-090 (4.12.3.5): if conversion char is 566 ** undefined, behavior is undefined. Print out the 567 ** character itself as printf(3) also does. 568 */ 569 default: 570 break; 571 } 572 } 573 if (pt == ptlim) 574 break; 575 *pt++ = *format; 576 } 577 return pt; 578 } 579 580 static char * 581 _conv(n, format, pt, ptlim) 582 const int n; 583 const char * const format; 584 char * const pt; 585 const char * const ptlim; 586 { 587 char buf[INT_STRLEN_MAXIMUM(int) + 1]; 588 589 (void) snprintf(buf, sizeof(buf), format, n); 590 return _add(buf, pt, ptlim); 591 } 592 593 static char * 594 _add(str, pt, ptlim) 595 const char * str; 596 char * pt; 597 const char * const ptlim; 598 { 599 while (pt < ptlim && (*pt = *str++) != '\0') 600 ++pt; 601 return pt; 602 } 603 604 /* 605 ** POSIX and the C Standard are unclear or inconsistent about 606 ** what %C and %y do if the year is negative or exceeds 9999. 607 ** Use the convention that %C concatenated with %y yields the 608 ** same output as %Y, and that %Y contains at least 4 bytes, 609 ** with more only if necessary. 610 */ 611 612 static char * 613 _yconv(a, b, convert_top, convert_yy, pt, ptlim) 614 const int a; 615 const int b; 616 const int convert_top; 617 const int convert_yy; 618 char * pt; 619 const char * const ptlim; 620 { 621 register int lead; 622 register int trail; 623 624 #define DIVISOR 100 625 trail = a % DIVISOR + b % DIVISOR; 626 lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR; 627 trail %= DIVISOR; 628 if (trail < 0 && lead > 0) { 629 trail += DIVISOR; 630 --lead; 631 } else if (lead < 0 && trail > 0) { 632 trail -= DIVISOR; 633 ++lead; 634 } 635 if (convert_top) { 636 if (lead == 0 && trail < 0) 637 pt = _add("-0", pt, ptlim); 638 else pt = _conv(lead, "%02d", pt, ptlim); 639 } 640 if (convert_yy) 641 pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim); 642 return pt; 643 } 644 645 #ifdef LOCALE_HOME 646 static struct lc_time_T * 647 _loc(void) 648 { 649 static const char locale_home[] = LOCALE_HOME; 650 static const char lc_time[] = "LC_TIME"; 651 static char * locale_buf; 652 653 int fd; 654 int oldsun; /* "...ain't got nothin' to do..." */ 655 char * lbuf; 656 char * name; 657 char * p; 658 const char ** ap; 659 const char * plim; 660 char filename[FILENAME_MAX]; 661 struct stat st; 662 size_t namesize; 663 size_t bufsize; 664 665 /* 666 ** Use localebuf.mon[0] to signal whether locale is already set up. 667 */ 668 if (localebuf.mon[0]) 669 return &localebuf; 670 name = setlocale(LC_TIME, (char *) NULL); 671 if (name == NULL || *name == '\0') 672 goto no_locale; 673 /* 674 ** If the locale name is the same as our cache, use the cache. 675 */ 676 lbuf = locale_buf; 677 if (lbuf != NULL && strcmp(name, lbuf) == 0) { 678 p = lbuf; 679 for (ap = (const char **) &localebuf; 680 ap < (const char **) (&localebuf + 1); 681 ++ap) 682 *ap = p += strlen(p) + 1; 683 return &localebuf; 684 } 685 /* 686 ** Slurp the locale file into the cache. 687 */ 688 namesize = strlen(name) + 1; 689 if (sizeof filename < 690 ((sizeof locale_home) + namesize + (sizeof lc_time))) 691 goto no_locale; 692 oldsun = 0; 693 (void) sprintf(filename, "%s/%s/%s", locale_home, name, lc_time); 694 fd = open(filename, O_RDONLY); 695 if (fd < 0) { 696 /* 697 ** Old Sun systems have a different naming and data convention. 698 */ 699 oldsun = 1; 700 (void) sprintf(filename, "%s/%s/%s", locale_home, 701 lc_time, name); 702 fd = open(filename, O_RDONLY); 703 if (fd < 0) 704 goto no_locale; 705 } 706 if (fstat(fd, &st) != 0) 707 goto bad_locale; 708 if (st.st_size <= 0) 709 goto bad_locale; 710 bufsize = namesize + st.st_size; 711 locale_buf = NULL; 712 lbuf = (lbuf == NULL) ? malloc(bufsize) : realloc(lbuf, bufsize); 713 if (lbuf == NULL) 714 goto bad_locale; 715 (void) strcpy(lbuf, name); 716 p = lbuf + namesize; 717 plim = p + st.st_size; 718 if (read(fd, p, (size_t) st.st_size) != st.st_size) 719 goto bad_lbuf; 720 if (close(fd) != 0) 721 goto bad_lbuf; 722 /* 723 ** Parse the locale file into localebuf. 724 */ 725 if (plim[-1] != '\n') 726 goto bad_lbuf; 727 for (ap = (const char **) &localebuf; 728 ap < (const char **) (&localebuf + 1); 729 ++ap) { 730 if (p == plim) 731 goto bad_lbuf; 732 *ap = p; 733 while (*p != '\n') 734 ++p; 735 *p++ = '\0'; 736 } 737 if (oldsun) { 738 /* 739 ** SunOS 4 used an obsolescent format; see localdtconv(3). 740 ** c_fmt had the ``short format for dates and times together'' 741 ** (SunOS 4 date, "%a %b %e %T %Z %Y" in the C locale); 742 ** date_fmt had the ``long format for dates'' 743 ** (SunOS 4 strftime %C, "%A, %B %e, %Y" in the C locale). 744 ** Discard the latter in favor of the former. 745 */ 746 localebuf.date_fmt = localebuf.c_fmt; 747 } 748 /* 749 ** Record the successful parse in the cache. 750 */ 751 locale_buf = lbuf; 752 753 return &localebuf; 754 755 bad_lbuf: 756 free(lbuf); 757 bad_locale: 758 (void) close(fd); 759 no_locale: 760 localebuf = C_time_locale; 761 locale_buf = NULL; 762 return &localebuf; 763 } 764 #endif /* defined LOCALE_HOME */ 765