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