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