gonzui


Format: Advanced Search

t2ex/bsd_source/lib/libc/src_bsd/time/strftime.cbare sourcepermlink (0.07 seconds)

Search this content:

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