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