• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Convert a broken-down time stamp to a string.  */
2 
3 /* Copyright 1989 The Regents of the University of California.
4    All rights reserved.
5 
6    Redistribution and use in source and binary forms, with or without
7    modification, are permitted provided that the following conditions
8    are met:
9    1. Redistributions of source code must retain the above copyright
10       notice, this list of conditions and the following disclaimer.
11    2. Redistributions in binary form must reproduce the above copyright
12       notice, this list of conditions and the following disclaimer in the
13       documentation and/or other materials provided with the distribution.
14    3. Neither the name of the University nor the names of its contributors
15       may be used to endorse or promote products derived from this software
16       without specific prior written permission.
17 
18    THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
19    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21    ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24    OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27    OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28    SUCH DAMAGE.  */
29 
30 /*
31 ** Based on the UCB version with the copyright notice appearing above.
32 **
33 ** This is ANSIish only when "multibyte character == plain character".
34 */
35 
36 #include "private.h"
37 
38 #include "tzfile.h"
39 #include "fcntl.h"
40 #include "locale.h"
41 
42 #if defined(__BIONIC__)
43 
44 /* LP32 had a 32-bit time_t, so we need to work around that here. */
45 #if defined(__LP64__)
46 #define time64_t time_t
47 #define mktime64 mktime
48 #else
49 #include <time64.h>
50 #endif
51 
52 #include <ctype.h>
53 
54 #endif
55 
56 struct lc_time_T {
57     const char *    mon[MONSPERYEAR];
58     const char *    month[MONSPERYEAR];
59     const char *    wday[DAYSPERWEEK];
60     const char *    weekday[DAYSPERWEEK];
61     const char *    X_fmt;
62     const char *    x_fmt;
63     const char *    c_fmt;
64     const char *    am;
65     const char *    pm;
66     const char *    date_fmt;
67 };
68 
69 #define Locale  (&C_time_locale)
70 
71 static const struct lc_time_T   C_time_locale = {
72     {
73         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
74         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
75     }, {
76         "January", "February", "March", "April", "May", "June",
77         "July", "August", "September", "October", "November", "December"
78     }, {
79         "Sun", "Mon", "Tue", "Wed",
80         "Thu", "Fri", "Sat"
81     }, {
82         "Sunday", "Monday", "Tuesday", "Wednesday",
83         "Thursday", "Friday", "Saturday"
84     },
85 
86     /* X_fmt */
87     "%H:%M:%S",
88 
89     /*
90     ** x_fmt
91     ** C99 requires this format.
92     ** Using just numbers (as here) makes Quakers happier;
93     ** it's also compatible with SVR4.
94     */
95     "%m/%d/%y",
96 
97     /*
98     ** c_fmt
99     ** C99 requires this format.
100     ** Previously this code used "%D %X", but we now conform to C99.
101     ** Note that
102     **  "%a %b %d %H:%M:%S %Y"
103     ** is used by Solaris 2.3.
104     */
105     "%a %b %e %T %Y",
106 
107     /* am */
108     "AM",
109 
110     /* pm */
111     "PM",
112 
113     /* date_fmt */
114     "%a %b %e %H:%M:%S %Z %Y"
115 };
116 
117 static char *   _add(const char *, char *, const char *, int);
118 static char *   _conv(int, const char *, char *, const char *);
119 static char *   _fmt(const char *, const struct tm *, char *, const char *,
120             int *);
121 static char *   _yconv(int, int, bool, bool, char *, const char *, int);
122 
123 #if !HAVE_POSIX_DECLS
124 extern char *   tzname[];
125 #endif
126 
127 #ifndef YEAR_2000_NAME
128 #define YEAR_2000_NAME  "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS"
129 #endif /* !defined YEAR_2000_NAME */
130 
131 #define IN_NONE 0
132 #define IN_SOME 1
133 #define IN_THIS 2
134 #define IN_ALL  3
135 
136 #if HAVE_STRFTIME_L
137 size_t
strftime_l(char * s,size_t maxsize,char const * format,struct tm const * t,locale_t locale)138 strftime_l(char *s, size_t maxsize, char const *format, struct tm const *t,
139 	   locale_t locale)
140 {
141   /* Just call strftime, as only the C locale is supported.  */
142   return strftime(s, maxsize, format, t);
143 }
144 #endif
145 
146 #define FORCE_LOWER_CASE 0x100 /* Android extension. */
147 
148 size_t
strftime(char * s,size_t maxsize,const char * format,const struct tm * t)149 strftime(char *s, size_t maxsize, const char *format, const struct tm *t)
150 {
151     char *  p;
152     int warn;
153 
154     tzset();
155     warn = IN_NONE;
156     p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn);
157 #ifndef NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU
158     if (warn != IN_NONE && getenv(YEAR_2000_NAME) != NULL) {
159         fprintf(stderr, "\n");
160         if (format == NULL)
161             fprintf(stderr, "NULL strftime format ");
162         else    fprintf(stderr, "strftime format \"%s\" ",
163                 format);
164         fprintf(stderr, "yields only two digits of years in ");
165         if (warn == IN_SOME)
166             fprintf(stderr, "some locales");
167         else if (warn == IN_THIS)
168             fprintf(stderr, "the current locale");
169         else    fprintf(stderr, "all locales");
170         fprintf(stderr, "\n");
171     }
172 #endif /* !defined NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU */
173     if (p == s + maxsize)
174         return 0;
175     *p = '\0';
176     return p - s;
177 }
178 
getformat(int modifier,char * normal,char * underscore,char * dash,char * zero)179 static char *getformat(int modifier, char *normal, char *underscore,
180                        char *dash, char *zero) {
181     switch (modifier) {
182     case '_':
183         return underscore;
184     case '-':
185         return dash;
186     case '0':
187         return zero;
188     }
189     return normal;
190 }
191 
192 static char *
_fmt(const char * format,const struct tm * t,char * pt,const char * ptlim,int * warnp)193 _fmt(const char *format, const struct tm *t, char *pt,
194         const char *ptlim, int *warnp)
195 {
196     for ( ; *format; ++format) {
197         if (*format == '%') {
198             int modifier = 0;
199 label:
200             switch (*++format) {
201             case '\0':
202                 --format;
203                 break;
204             case 'A':
205                 pt = _add((t->tm_wday < 0 ||
206                     t->tm_wday >= DAYSPERWEEK) ?
207                     "?" : Locale->weekday[t->tm_wday],
208                     pt, ptlim, modifier);
209                 continue;
210             case 'a':
211                 pt = _add((t->tm_wday < 0 ||
212                     t->tm_wday >= DAYSPERWEEK) ?
213                     "?" : Locale->wday[t->tm_wday],
214                     pt, ptlim, modifier);
215                 continue;
216             case 'B':
217                 pt = _add((t->tm_mon < 0 ||
218                                 t->tm_mon >= MONSPERYEAR) ?
219                                 "?" : Locale->month[t->tm_mon],
220                                 pt, ptlim, modifier);
221                 continue;
222             case 'b':
223             case 'h':
224                 pt = _add((t->tm_mon < 0 ||
225                     t->tm_mon >= MONSPERYEAR) ?
226                     "?" : Locale->mon[t->tm_mon],
227                     pt, ptlim, modifier);
228                 continue;
229             case 'C':
230                 /*
231                 ** %C used to do a...
232                 **  _fmt("%a %b %e %X %Y", t);
233                 ** ...whereas now POSIX 1003.2 calls for
234                 ** something completely different.
235                 ** (ado, 1993-05-24)
236                 */
237                 pt = _yconv(t->tm_year, TM_YEAR_BASE,
238                     true, false, pt, ptlim, modifier);
239                 continue;
240             case 'c':
241                 {
242                 int warn2 = IN_SOME;
243 
244                 pt = _fmt(Locale->c_fmt, t, pt, ptlim, &warn2);
245                 if (warn2 == IN_ALL)
246                     warn2 = IN_THIS;
247                 if (warn2 > *warnp)
248                     *warnp = warn2;
249                 }
250                 continue;
251             case 'D':
252                                 pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp);
253                 continue;
254             case 'd':
255                                 pt = _conv(t->tm_mday, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
256                 continue;
257             case 'E':
258             case 'O':
259                 /*
260                 ** C99 locale modifiers.
261                 ** The sequences
262                 **  %Ec %EC %Ex %EX %Ey %EY
263                 **  %Od %oe %OH %OI %Om %OM
264                 **  %OS %Ou %OU %OV %Ow %OW %Oy
265                 ** are supposed to provide alternate
266                 ** representations.
267                 */
268                 goto label;
269             case '_':
270             case '-':
271             case '0':
272             case '^':
273             case '#':
274                 modifier = *format;
275                 goto label;
276             case 'e':
277                 pt = _conv(t->tm_mday, getformat(modifier, "%2d", "%2d", "%d", "%02d"), pt, ptlim);
278                 continue;
279             case 'F':
280                 pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp);
281                 continue;
282             case 'H':
283                 pt = _conv(t->tm_hour, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
284                 continue;
285             case 'I':
286                 pt = _conv((t->tm_hour % 12) ?
287                     (t->tm_hour % 12) : 12,
288                     getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
289                 continue;
290             case 'j':
291                 pt = _conv(t->tm_yday + 1, getformat(modifier, "%03d", "%3d", "%d", "%03d"), pt, ptlim);
292                 continue;
293             case 'k':
294                 /*
295                 ** This used to be...
296                 **  _conv(t->tm_hour % 12 ?
297                 **      t->tm_hour % 12 : 12, 2, ' ');
298                 ** ...and has been changed to the below to
299                 ** match SunOS 4.1.1 and Arnold Robbins'
300                 ** strftime version 3.0. That is, "%k" and
301                 ** "%l" have been swapped.
302                 ** (ado, 1993-05-24)
303                 */
304                 pt = _conv(t->tm_hour, getformat(modifier, "%2d", "%2d", "%d", "%02d"), pt, ptlim);
305                 continue;
306 #ifdef KITCHEN_SINK
307             case 'K':
308                 /*
309                 ** After all this time, still unclaimed!
310                 */
311                 pt = _add("kitchen sink", pt, ptlim);
312                 continue;
313 #endif /* defined KITCHEN_SINK */
314             case 'l':
315                 /*
316                 ** This used to be...
317                 **  _conv(t->tm_hour, 2, ' ');
318                 ** ...and has been changed to the below to
319                 ** match SunOS 4.1.1 and Arnold Robbin's
320                 ** strftime version 3.0. That is, "%k" and
321                 ** "%l" have been swapped.
322                 ** (ado, 1993-05-24)
323                 */
324                 pt = _conv((t->tm_hour % 12) ?
325                     (t->tm_hour % 12) : 12,
326                     getformat(modifier, "%2d", "%2d", "%d", "%02d"), pt, ptlim);
327                 continue;
328             case 'M':
329                 pt = _conv(t->tm_min, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
330                 continue;
331             case 'm':
332                 pt = _conv(t->tm_mon + 1, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
333                 continue;
334             case 'n':
335                 pt = _add("\n", pt, ptlim, modifier);
336                 continue;
337             case 'P':
338             case 'p':
339                 pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
340                     Locale->pm :
341                     Locale->am,
342                     pt, ptlim, (*format == 'P') ? FORCE_LOWER_CASE : modifier);
343                 continue;
344             case 'R':
345                 pt = _fmt("%H:%M", t, pt, ptlim, warnp);
346                 continue;
347             case 'r':
348                 pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp);
349                 continue;
350             case 'S':
351                 pt = _conv(t->tm_sec, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
352                 continue;
353             case 's':
354                 {
355                     struct tm   tm;
356                     char        buf[INT_STRLEN_MAXIMUM(
357                                 time64_t) + 1];
358                     time64_t    mkt;
359 
360                     tm = *t;
361                     mkt = mktime64(&tm);
362                     if (TYPE_SIGNED(time64_t))
363                         snprintf(buf, sizeof(buf), "%"PRIdMAX,
364                                  (intmax_t) mkt);
365                     else    snprintf(buf, sizeof(buf), "%"PRIuMAX,
366                                      (uintmax_t) mkt);
367                     pt = _add(buf, pt, ptlim, modifier);
368                 }
369                 continue;
370             case 'T':
371                 pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp);
372                 continue;
373             case 't':
374                 pt = _add("\t", pt, ptlim, modifier);
375                 continue;
376             case 'U':
377                 pt = _conv((t->tm_yday + DAYSPERWEEK -
378                     t->tm_wday) / DAYSPERWEEK,
379                     getformat(modifier, "%02d", "%2d", "%d", "%02d"), 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,
390                     "%d", pt, ptlim);
391                 continue;
392             case 'V':   /* ISO 8601 week number */
393             case 'G':   /* ISO 8601 year (four digits) */
394             case 'g':   /* ISO 8601 year (two digits) */
395 /*
396 ** From Arnold Robbins' strftime version 3.0: "the week number of the
397 ** year (the first Monday as the first day of week 1) as a decimal number
398 ** (01-53)."
399 ** (ado, 1993-05-24)
400 **
401 ** From <http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html> by Markus Kuhn:
402 ** "Week 01 of a year is per definition the first week which has the
403 ** Thursday in this year, which is equivalent to the week which contains
404 ** the fourth day of January. In other words, the first week of a new year
405 ** is the week which has the majority of its days in the new year. Week 01
406 ** might also contain days from the previous year and the week before week
407 ** 01 of a year is the last week (52 or 53) of the previous year even if
408 ** it contains days from the new year. A week starts with Monday (day 1)
409 ** and ends with Sunday (day 7). For example, the first week of the year
410 ** 1997 lasts from 1996-12-30 to 1997-01-05..."
411 ** (ado, 1996-01-02)
412 */
413                 {
414                     int year;
415                     int base;
416                     int yday;
417                     int wday;
418                     int w;
419 
420                     year = t->tm_year;
421                     base = TM_YEAR_BASE;
422                     yday = t->tm_yday;
423                     wday = t->tm_wday;
424                     for ( ; ; ) {
425                         int len;
426                         int bot;
427                         int top;
428 
429                         len = isleap_sum(year, base) ?
430                             DAYSPERLYEAR :
431                             DAYSPERNYEAR;
432                         /*
433                         ** What yday (-3 ... 3) does
434                         ** the ISO year begin on?
435                         */
436                         bot = ((yday + 11 - wday) %
437                             DAYSPERWEEK) - 3;
438                         /*
439                         ** What yday does the NEXT
440                         ** ISO year begin on?
441                         */
442                         top = bot -
443                             (len % DAYSPERWEEK);
444                         if (top < -3)
445                             top += DAYSPERWEEK;
446                         top += len;
447                         if (yday >= top) {
448                             ++base;
449                             w = 1;
450                             break;
451                         }
452                         if (yday >= bot) {
453                             w = 1 + ((yday - bot) /
454                                 DAYSPERWEEK);
455                             break;
456                         }
457                         --base;
458                         yday += isleap_sum(year, base) ?
459                             DAYSPERLYEAR :
460                             DAYSPERNYEAR;
461                     }
462 #ifdef XPG4_1994_04_09
463                     if ((w == 52 &&
464                         t->tm_mon == TM_JANUARY) ||
465                         (w == 1 &&
466                         t->tm_mon == TM_DECEMBER))
467                             w = 53;
468 #endif /* defined XPG4_1994_04_09 */
469                     if (*format == 'V')
470                         pt = _conv(w, getformat(modifier, "%02d", "%2d", "%d", "%02d"),
471                                pt, ptlim);
472                     else if (*format == 'g') {
473                         *warnp = IN_ALL;
474                         pt = _yconv(year, base,
475                             false, true,
476                             pt, ptlim, modifier);
477                     } else  pt = _yconv(year, base,
478                             true, true,
479                             pt, ptlim, modifier);
480                 }
481                 continue;
482             case 'v':
483                 /*
484                 ** From Arnold Robbins' strftime version 3.0:
485                 ** "date as dd-bbb-YYYY"
486                 ** (ado, 1993-05-24)
487                 */
488                 pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp);
489                 continue;
490             case 'W':
491                 pt = _conv((t->tm_yday + DAYSPERWEEK -
492                     (t->tm_wday ?
493                     (t->tm_wday - 1) :
494                     (DAYSPERWEEK - 1))) / DAYSPERWEEK,
495                     getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
496                 continue;
497             case 'w':
498                 pt = _conv(t->tm_wday, "%d", pt, ptlim);
499                 continue;
500             case 'X':
501                 pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp);
502                 continue;
503             case 'x':
504                 {
505                 int warn2 = IN_SOME;
506 
507                 pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2);
508                 if (warn2 == IN_ALL)
509                     warn2 = IN_THIS;
510                 if (warn2 > *warnp)
511                     *warnp = warn2;
512                 }
513                 continue;
514             case 'y':
515                 *warnp = IN_ALL;
516                 pt = _yconv(t->tm_year, TM_YEAR_BASE,
517                     false, true,
518                     pt, ptlim, modifier);
519                 continue;
520             case 'Y':
521                 pt = _yconv(t->tm_year, TM_YEAR_BASE,
522                     true, true,
523                     pt, ptlim, modifier);
524                 continue;
525             case 'Z':
526 #ifdef TM_ZONE
527                 // BEGIN: Android-changed.
528                 {
529                     const char* zone = t->TM_ZONE;
530                     if (!zone || !*zone) {
531                         // "The value of tm_isdst shall be positive if Daylight Savings Time is
532                         // in effect, 0 if Daylight Savings Time is not in effect, and negative
533                         // if the information is not available."
534                         if (t->tm_isdst == 0) zone = tzname[0];
535                         else if (t->tm_isdst > 0) zone = tzname[1];
536 
537                         // "Replaced by the timezone name or abbreviation, or by no bytes if no
538                         // timezone information exists."
539                         if (!zone || !*zone) zone = "";
540                     }
541                     pt = _add(zone, pt, ptlim, modifier);
542                 }
543                 // END: Android-changed.
544 #else
545                 if (t->tm_isdst >= 0)
546                     pt = _add(tzname[t->tm_isdst != 0],
547                         pt, ptlim);
548 #endif
549                 /*
550                 ** C99 says that %Z must be replaced by the
551                 ** empty string if the time zone is not
552                 ** determinable.
553                 */
554                 continue;
555             case 'z':
556                 {
557                 long     diff;
558                 char const *    sign;
559 
560                 if (t->tm_isdst < 0)
561                     continue;
562 #ifdef TM_GMTOFF
563                 diff = t->TM_GMTOFF;
564 #else /* !defined TM_GMTOFF */
565                 /*
566                 ** C99 says that the UT offset must
567                 ** be computed by looking only at
568                 ** tm_isdst. This requirement is
569                 ** incorrect, since it means the code
570                 ** must rely on magic (in this case
571                 ** altzone and timezone), and the
572                 ** magic might not have the correct
573                 ** offset. Doing things correctly is
574                 ** tricky and requires disobeying C99;
575                 ** see GNU C strftime for details.
576                 ** For now, punt and conform to the
577                 ** standard, even though it's incorrect.
578                 **
579                 ** C99 says that %z must be replaced by the
580                 ** empty string if the time zone is not
581                 ** determinable, so output nothing if the
582                 ** appropriate variables are not available.
583                 */
584                 if (t->tm_isdst == 0)
585 #ifdef USG_COMPAT
586                     diff = -timezone;
587 #else /* !defined USG_COMPAT */
588                     continue;
589 #endif /* !defined USG_COMPAT */
590                 else
591 #ifdef ALTZONE
592                     diff = -altzone;
593 #else /* !defined ALTZONE */
594                     continue;
595 #endif /* !defined ALTZONE */
596 #endif /* !defined TM_GMTOFF */
597                 if (diff < 0) {
598                     sign = "-";
599                     diff = -diff;
600                 } else  sign = "+";
601                 pt = _add(sign, pt, ptlim, modifier);
602                 diff /= SECSPERMIN;
603                 diff = (diff / MINSPERHOUR) * 100 +
604                     (diff % MINSPERHOUR);
605                 pt = _conv(diff, getformat(modifier, "%04d", "%4d", "%d", "%04d"), pt, ptlim);
606                 }
607                 continue;
608             case '+':
609                 pt = _fmt(Locale->date_fmt, t, pt, ptlim,
610                     warnp);
611                 continue;
612             case '%':
613             /*
614             ** X311J/88-090 (4.12.3.5): if conversion char is
615             ** undefined, behavior is undefined. Print out the
616             ** character itself as printf(3) also does.
617             */
618             default:
619                 break;
620             }
621         }
622         if (pt == ptlim)
623             break;
624         *pt++ = *format;
625     }
626     return pt;
627 }
628 
629 static char *
_conv(int n,const char * format,char * pt,const char * ptlim)630 _conv(int n, const char *format, char *pt, const char *ptlim)
631 {
632 	char	buf[INT_STRLEN_MAXIMUM(int) + 1];
633 
634 	snprintf(buf, sizeof(buf), format, n);
635 	return _add(buf, pt, ptlim, 0);
636 }
637 
638 static char *
_add(const char * str,char * pt,const char * const ptlim,int modifier)639 _add(const char *str, char *pt, const char *const ptlim, int modifier)
640 {
641         int c;
642 
643         switch (modifier) {
644         case FORCE_LOWER_CASE:
645                 while (pt < ptlim && (*pt = tolower(*str++)) != '\0') {
646                         ++pt;
647                 }
648                 break;
649 
650         case '^':
651                 while (pt < ptlim && (*pt = toupper(*str++)) != '\0') {
652                         ++pt;
653                 }
654                 break;
655 
656         case '#':
657                 while (pt < ptlim && (c = *str++) != '\0') {
658                         if (isupper(c)) {
659                                 c = tolower(c);
660                         } else if (islower(c)) {
661                                 c = toupper(c);
662                         }
663                         *pt = c;
664                         ++pt;
665                 }
666 
667                 break;
668 
669         default:
670                 while (pt < ptlim && (*pt = *str++) != '\0') {
671                         ++pt;
672                 }
673         }
674 
675     return pt;
676 }
677 
678 /*
679 ** POSIX and the C Standard are unclear or inconsistent about
680 ** what %C and %y do if the year is negative or exceeds 9999.
681 ** Use the convention that %C concatenated with %y yields the
682 ** same output as %Y, and that %Y contains at least 4 bytes,
683 ** with more only if necessary.
684 */
685 
686 static char *
_yconv(int a,int b,bool convert_top,bool convert_yy,char * pt,const char * ptlim,int modifier)687 _yconv(int a, int b, bool convert_top, bool convert_yy,
688        char *pt, const char *ptlim, int modifier)
689 {
690     register int    lead;
691     register int    trail;
692 
693 #define DIVISOR 100
694     trail = a % DIVISOR + b % DIVISOR;
695     lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR;
696     trail %= DIVISOR;
697     if (trail < 0 && lead > 0) {
698         trail += DIVISOR;
699         --lead;
700     } else if (lead < 0 && trail > 0) {
701         trail -= DIVISOR;
702         ++lead;
703     }
704     if (convert_top) {
705         if (lead == 0 && trail < 0)
706             pt = _add("-0", pt, ptlim, modifier);
707         else    pt = _conv(lead, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
708     }
709     if (convert_yy)
710         pt = _conv(((trail < 0) ? -trail : trail), getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
711     return pt;
712 }
713