[BACK]Return to strftime.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / lib / libc / time

Annotation of src/lib/libc/time/strftime.c, Revision 1.21

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

CVSweb <webmaster@jp.NetBSD.org>