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