home *** CD-ROM | disk | FTP | other *** search
/ PC Extra Super CD 1998 January / PCPLUS131.iso / DJGPP / V2 / DJLSR201.ZIP / zoneinfo / src / strftime.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-08-12  |  14.4 KB  |  608 lines

  1. #ifndef lint
  2. #ifndef NOID
  3. static char    elsieid[] = "@(#)strftime.c    7.48";
  4. /*
  5. ** Based on the UCB version with the ID appearing below.
  6. ** This is ANSIish only when "multibyte character == plain character".
  7. */
  8. #endif /* !defined NOID */
  9. #endif /* !defined lint */
  10.  
  11. #include "private.h"
  12.  
  13. /*
  14. ** Copyright (c) 1989 The Regents of the University of California.
  15. ** All rights reserved.
  16. **
  17. ** Redistribution and use in source and binary forms are permitted
  18. ** provided that the above copyright notice and this paragraph are
  19. ** duplicated in all such forms and that any documentation,
  20. ** advertising materials, and other materials related to such
  21. ** distribution and use acknowledge that the software was developed
  22. ** by the University of California, Berkeley.  The name of the
  23. ** University may not be used to endorse or promote products derived
  24. ** from this software without specific prior written permission.
  25. ** THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  26. ** IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  27. ** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  28. */
  29.  
  30. #ifndef LIBC_SCCS
  31. #ifndef lint
  32. static const char    sccsid[] = "@(#)strftime.c    5.4 (Berkeley) 3/14/89";
  33. #endif /* !defined lint */
  34. #endif /* !defined LIBC_SCCS */
  35.  
  36. #include "tzfile.h"
  37. #include "fcntl.h"
  38. #include "locale.h"
  39.  
  40. struct lc_time_T {
  41.     const char *    mon[12];
  42.     const char *    month[12];
  43.     const char *    wday[7];
  44.     const char *    weekday[7];
  45.     const char *    X_fmt;
  46.     const char *    x_fmt;
  47.     const char *    c_fmt;
  48.     const char *    am;
  49.     const char *    pm;
  50.     const char *    date_fmt;
  51. };
  52.  
  53. #ifdef LOCALE_HOME
  54. #include "sys/stat.h"
  55. static struct lc_time_T        localebuf;
  56. static struct lc_time_T *    _loc P((void));
  57. #define Locale    _loc()
  58. #endif /* defined LOCALE_HOME */
  59. #ifndef LOCALE_HOME
  60. #define Locale    (&C_time_locale)
  61. #endif /* !defined LOCALE_HOME */
  62.  
  63. static const struct lc_time_T    C_time_locale = {
  64.     {
  65.         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  66.         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  67.     }, {
  68.         "January", "February", "March", "April", "May", "June",
  69.         "July", "August", "September", "October", "November", "December"
  70.     }, {
  71.         "Sun", "Mon", "Tue", "Wed",
  72.         "Thu", "Fri", "Sat"
  73.     }, {
  74.         "Sunday", "Monday", "Tuesday", "Wednesday",
  75.         "Thursday", "Friday", "Saturday"
  76.     },
  77.  
  78.     /* X_fmt */
  79.     "%H:%M:%S",
  80.  
  81.     /*
  82.     ** x_fmt
  83.     ** Since the C language standard calls for
  84.     ** "date, using locale's date format," anything goes.
  85.     ** Using just numbers (as here) makes Quakers happier;
  86.     ** it's also compatible with SVR4.
  87.     */
  88.     "%m/%d/%y",
  89.  
  90.     /*
  91.     ** c_fmt
  92.     ** Note that
  93.     **    "%a %b %d %H:%M:%S %Y"
  94.     ** is used by Solaris 2.3.
  95.     */
  96.     "%D %X",    /* %m/%d/%y %H:%M:%S */
  97.  
  98.     /* am */
  99.     "AM",
  100.  
  101.     /* pm */
  102.     "PM",
  103.  
  104.     /* date_fmt */
  105.     "%a %b %e %H:%M:%S %Z %Y"
  106. };
  107.  
  108. static char *    _add P((const char *, char *, const char *));
  109. static char *    _conv P((int, const char *, char *, const char *));
  110. static char *    _fmt P((const char *, const struct tm *, char *, const char *));
  111.  
  112. size_t strftime P((char *, size_t, const char *, const struct tm *));
  113.  
  114. extern char *    tzname[];
  115.  
  116. size_t
  117. strftime(s, maxsize, format, t)
  118. char * const        s;
  119. const size_t        maxsize;
  120. const char * const    format;
  121. const struct tm * const    t;
  122. {
  123.     char *    p;
  124.  
  125.     tzset();
  126. #ifdef LOCALE_HOME
  127.     localebuf.mon[0] = 0;
  128. #endif /* defined LOCALE_HOME */
  129.     p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize);
  130.     if (p == s + maxsize)
  131.         return 0;
  132.     *p = '\0';
  133.     return p - s;
  134. }
  135.  
  136. static char *
  137. _fmt(format, t, pt, ptlim)
  138. const char *        format;
  139. const struct tm * const    t;
  140. char *            pt;
  141. const char * const    ptlim;
  142. {
  143.     for ( ; *format; ++format) {
  144.         if (*format == '%') {
  145. label:
  146.             switch (*++format) {
  147.             case '\0':
  148.                 --format;
  149.                 break;
  150.             case 'A':
  151.                 pt = _add((t->tm_wday < 0 || t->tm_wday > 6) ?
  152.                     "?" : Locale->weekday[t->tm_wday],
  153.                     pt, ptlim);
  154.                 continue;
  155.             case 'a':
  156.                 pt = _add((t->tm_wday < 0 || t->tm_wday > 6) ?
  157.                     "?" : Locale->wday[t->tm_wday],
  158.                     pt, ptlim);
  159.                 continue;
  160.             case 'B':
  161.                 pt = _add((t->tm_mon < 0 || t->tm_mon > 11) ?
  162.                     "?" : Locale->month[t->tm_mon],
  163.                     pt, ptlim);
  164.                 continue;
  165.             case 'b':
  166.             case 'h':
  167.                 pt = _add((t->tm_mon < 0 || t->tm_mon > 11) ?
  168.                     "?" : Locale->mon[t->tm_mon],
  169.                     pt, ptlim);
  170.                 continue;
  171.             case 'C':
  172.                 /*
  173.                 ** %C used to do a...
  174.                 **    _fmt("%a %b %e %X %Y", t);
  175.                 ** ...whereas now POSIX 1003.2 calls for
  176.                 ** something completely different.
  177.                 ** (ado, 5/24/93)
  178.                 */
  179.                 pt = _conv((t->tm_year + TM_YEAR_BASE) / 100,
  180.                     "%02d", pt, ptlim);
  181.                 continue;
  182.             case 'c':
  183.                 pt = _fmt(Locale->c_fmt, t, pt, ptlim);
  184.                 continue;
  185.             case 'D':
  186.                 pt = _fmt("%m/%d/%y", t, pt, ptlim);
  187.                 continue;
  188.             case 'd':
  189.                 pt = _conv(t->tm_mday, "%02d", pt, ptlim);
  190.                 continue;
  191.             case 'E':
  192.             case 'O':
  193.                 /*
  194.                 ** POSIX locale extensions, a la
  195.                 ** Arnold Robbins' strftime version 3.0.
  196.                 ** The sequences
  197.                 **    %Ec %EC %Ex %Ey %EY
  198.                 **    %Od %oe %OH %OI %Om %OM
  199.                 **    %OS %Ou %OU %OV %Ow %OW %Oy
  200.                 ** are supposed to provide alternate
  201.                 ** representations.
  202.                 ** (ado, 5/24/93)
  203.                 */
  204.                 goto label;
  205.             case 'e':
  206.                 pt = _conv(t->tm_mday, "%2d", pt, ptlim);
  207.                 continue;
  208.             case 'H':
  209.                 pt = _conv(t->tm_hour, "%02d", pt, ptlim);
  210.                 continue;
  211.             case 'I':
  212.                 pt = _conv((t->tm_hour % 12) ?
  213.                     (t->tm_hour % 12) : 12,
  214.                     "%02d", pt, ptlim);
  215.                 continue;
  216.             case 'j':
  217.                 pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim);
  218.                 continue;
  219.             case 'k':
  220.                 /*
  221.                 ** This used to be...
  222.                 **    _conv(t->tm_hour % 12 ?
  223.                 **        t->tm_hour % 12 : 12, 2, ' ');
  224.                 ** ...and has been changed to the below to
  225.                 ** match SunOS 4.1.1 and Arnold Robbins'
  226.                 ** strftime version 3.0.  That is, "%k" and
  227.                 ** "%l" have been swapped.
  228.                 ** (ado, 5/24/93)
  229.                 */
  230.                 pt = _conv(t->tm_hour, "%2d", pt, ptlim);
  231.                 continue;
  232. #ifdef KITCHEN_SINK
  233.             case 'K':
  234.                 /*
  235.                 ** After all this time, still unclaimed!
  236.                 */
  237.                 pt = _add("kitchen sink", pt, ptlim);
  238.                 continue;
  239. #endif /* defined KITCHEN_SINK */
  240.             case 'l':
  241.                 /*
  242.                 ** This used to be...
  243.                 **    _conv(t->tm_hour, 2, ' ');
  244.                 ** ...and has been changed to the below to
  245.                 ** match SunOS 4.1.1 and Arnold Robbin's
  246.                 ** strftime version 3.0.  That is, "%k" and
  247.                 ** "%l" have been swapped.
  248.                 ** (ado, 5/24/93)
  249.                 */
  250.                 pt = _conv((t->tm_hour % 12) ?
  251.                     (t->tm_hour % 12) : 12,
  252.                     "%2d", pt, ptlim);
  253.                 continue;
  254.             case 'M':
  255.                 pt = _conv(t->tm_min, "%02d", pt, ptlim);
  256.                 continue;
  257.             case 'm':
  258.                 pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim);
  259.                 continue;
  260.             case 'n':
  261.                 pt = _add("\n", pt, ptlim);
  262.                 continue;
  263.             case 'p':
  264.                 pt = _add((t->tm_hour >= 12) ?
  265.                     Locale->pm :
  266.                     Locale->am,
  267.                     pt, ptlim);
  268.                 continue;
  269.             case 'R':
  270.                 pt = _fmt("%H:%M", t, pt, ptlim);
  271.                 continue;
  272.             case 'r':
  273.                 pt = _fmt("%I:%M:%S %p", t, pt, ptlim);
  274.                 continue;
  275.             case 'S':
  276.                 pt = _conv(t->tm_sec, "%02d", pt, ptlim);
  277.                 continue;
  278.             case 's':
  279.                 {
  280.                     struct tm    tm;
  281.                     char        buf[INT_STRLEN_MAXIMUM(
  282.                                 time_t) + 1];
  283.                     time_t        mkt;
  284.  
  285.                     tm = *t;
  286.                     mkt = mktime(&tm);
  287.                     if (TYPE_SIGNED(time_t))
  288.                         (void) sprintf(buf, "%ld",
  289.                             (long) mkt);
  290.                     else    (void) sprintf(buf, "%lu",
  291.                             (unsigned long) mkt);
  292.                     pt = _add(buf, pt, ptlim);
  293.                 }
  294.                 continue;
  295.             case 'T':
  296.                 pt = _fmt("%H:%M:%S", t, pt, ptlim);
  297.                 continue;
  298.             case 't':
  299.                 pt = _add("\t", pt, ptlim);
  300.                 continue;
  301.             case 'U':
  302.                 pt = _conv((t->tm_yday + 7 - t->tm_wday) / 7,
  303.                     "%02d", pt, ptlim);
  304.                 continue;
  305.             case 'u':
  306.                 /*
  307.                 ** From Arnold Robbins' strftime version 3.0:
  308.                 ** "ISO 8601: Weekday as a decimal number
  309.                 ** [1 (Monday) - 7]"
  310.                 ** (ado, 5/24/93)
  311.                 */
  312.                 pt = _conv((t->tm_wday == 0) ? 7 : t->tm_wday,
  313.                     "%d", pt, ptlim);
  314.                 continue;
  315.             case 'V':    /* ISO 8601 week number */
  316.             case 'G':    /* ISO 8601 year (four digits) */
  317.             case 'g':    /* ISO 8601 year (two digits) */
  318. /*
  319. ** From Arnold Robbins' strftime version 3.0:  "the week number of the
  320. ** year (the first Monday as the first day of week 1) as a decimal number
  321. ** (01-53)."
  322. ** (ado, 1993-05-24)
  323. **
  324. ** From "http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html" by Markus Kuhn:
  325. ** "Week 01 of a year is per definition the first week which has the
  326. ** Thursday in this year, which is equivalent to the week which contains
  327. ** the fourth day of January. In other words, the first week of a new year
  328. ** is the week which has the majority of its days in the new year. Week 01
  329. ** might also contain days from the previous year and the week before week
  330. ** 01 of a year is the last week (52 or 53) of the previous year even if
  331. ** it contains days from the new year. A week starts with Monday (day 1)
  332. ** and ends with Sunday (day 7).  For example, the first week of the year
  333. ** 1997 lasts from 1996-12-30 to 1997-01-05..."
  334. ** (ado, 1996-01-02)
  335. */
  336.                 {
  337.                     int    year;
  338.                     int    yday;
  339.                     int    wday;
  340.                     int    w;
  341.  
  342.                     year = t->tm_year + TM_YEAR_BASE;
  343.                     yday = t->tm_yday;
  344.                     wday = t->tm_wday;
  345.                     for ( ; ; ) {
  346.                         int    len;
  347.                         int    bot;
  348.                         int    top;
  349.  
  350.                         len = isleap(year) ?
  351.                             DAYSPERLYEAR :
  352.                             DAYSPERNYEAR;
  353.                         /*
  354.                         ** What yday (-3 ... 3) does
  355.                         ** the ISO year begin on?
  356.                         */
  357.                         bot = ((yday + 11 - wday) %
  358.                             DAYSPERWEEK) - 3;
  359.                         /*
  360.                         ** What yday does the NEXT
  361.                         ** ISO year begin on?
  362.                         */
  363.                         top = bot -
  364.                             (len % DAYSPERWEEK);
  365.                         if (top < -3)
  366.                             top += DAYSPERWEEK;
  367.                         top += len;
  368.                         if (yday >= top) {
  369.                             ++year;
  370.                             w = 1;
  371.                             break;
  372.                         }
  373.                         if (yday >= bot) {
  374.                             w = 1 + ((yday - bot) /
  375.                                 DAYSPERWEEK);
  376.                             break;
  377.                         }
  378.                         --year;
  379.                         yday += isleap(year) ?
  380.                             DAYSPERLYEAR :
  381.                             DAYSPERNYEAR;
  382.                     }
  383. #ifdef XPG4_1994_04_09
  384.                     if ((w == 52
  385.                          && t->tm_mon == TM_JANUARY)
  386.                         || (w == 1
  387.                         && t->tm_mon == TM_DECEMBER))
  388.                         w = 53;
  389. #endif /* defined XPG4_1994_04_09 */
  390.                     if (*format == 'V')
  391.                         pt = _conv(w, "%02d",
  392.                             pt, ptlim);
  393.                     else if (*format == 'G')
  394.                         pt = _conv(year, "%02d",
  395.                             pt, ptlim);
  396.                     else    pt = _conv(year, "%04d",
  397.                             pt, ptlim);
  398.                 }
  399.                 continue;
  400.             case 'v':
  401.                 /*
  402.                 ** From Arnold Robbins' strftime version 3.0:
  403.                 ** "date as dd-bbb-YYYY"
  404.                 ** (ado, 5/24/93)
  405.                 */
  406.                 pt = _fmt("%e-%b-%Y", t, pt, ptlim);
  407.                 continue;
  408.             case 'W':
  409.                 pt = _conv((t->tm_yday + 7 -
  410.                     (t->tm_wday ?
  411.                     (t->tm_wday - 1) : 6)) / 7,
  412.                     "%02d", pt, ptlim);
  413.                 continue;
  414.             case 'w':
  415.                 pt = _conv(t->tm_wday, "%d", pt, ptlim);
  416.                 continue;
  417.             case 'X':
  418.                 pt = _fmt(Locale->X_fmt, t, pt, ptlim);
  419.                 continue;
  420.             case 'x':
  421.                 pt = _fmt(Locale->x_fmt, t, pt, ptlim);
  422.                 continue;
  423.             case 'y':
  424.                 pt = _conv((t->tm_year + TM_YEAR_BASE) % 100,
  425.                     "%02d", pt, ptlim);
  426.                 continue;
  427.             case 'Y':
  428.                 pt = _conv(t->tm_year + TM_YEAR_BASE, "%04d",
  429.                     pt, ptlim);
  430.                 continue;
  431.             case 'Z':
  432. #ifdef TM_ZONE
  433.                 if (t->TM_ZONE != NULL)
  434.                     pt = _add(t->TM_ZONE, pt, ptlim);
  435.                 else
  436. #endif /* defined TM_ZONE */
  437.                 if (t->tm_isdst == 0 || t->tm_isdst == 1) {
  438.                     pt = _add(tzname[t->tm_isdst],
  439.                         pt, ptlim);
  440.                 } else  pt = _add("?", pt, ptlim);
  441.                 continue;
  442.             case '+':
  443.                 pt = _fmt(Locale->date_fmt, t, pt, ptlim);
  444.                 continue;
  445.             case '%':
  446.             /*
  447.              * X311J/88-090 (4.12.3.5): if conversion char is
  448.              * undefined, behavior is undefined.  Print out the
  449.              * character itself as printf(3) also does.
  450.              */
  451.             default:
  452.                 break;
  453.             }
  454.         }
  455.         if (pt == ptlim)
  456.             break;
  457.         *pt++ = *format;
  458.     }
  459.     return pt;
  460. }
  461.  
  462. static char *
  463. _conv(n, format, pt, ptlim)
  464. const int        n;
  465. const char * const    format;
  466. char * const        pt;
  467. const char * const    ptlim;
  468. {
  469.     char    buf[INT_STRLEN_MAXIMUM(int) + 1];
  470.  
  471.     (void) sprintf(buf, format, n);
  472.     return _add(buf, pt, ptlim);
  473. }
  474.  
  475. static char *
  476. _add(str, pt, ptlim)
  477. const char *        str;
  478. char *            pt;
  479. const char * const    ptlim;
  480. {
  481.     while (pt < ptlim && (*pt = *str++) != '\0')
  482.         ++pt;
  483.     return pt;
  484. }
  485.  
  486. #ifdef LOCALE_HOME
  487. static struct lc_time_T *
  488. _loc P((void))
  489. {
  490.     static const char    locale_home[] = LOCALE_HOME;
  491.     static const char    lc_time[] = "LC_TIME";
  492.     static char *        locale_buf;
  493.     static char        locale_buf_C[] = "C";
  494.  
  495.     int            fd;
  496.     int            oldsun;    /* "...ain't got nothin' to do..." */
  497.     char *            lbuf;
  498.     char *            name;
  499.     char *            p;
  500.     const char **        ap;
  501.     const char *        plim;
  502.     char            filename[FILENAME_MAX];
  503.     struct stat        st;
  504.     size_t            namesize;
  505.     size_t            bufsize;
  506.  
  507.     /*
  508.     ** Use localebuf.mon[0] to signal whether locale is already set up.
  509.     */
  510.     if (localebuf.mon[0])
  511.         return &localebuf;
  512.     name = setlocale(LC_TIME, (char *) NULL);
  513.     if (name == NULL || *name == '\0')
  514.         goto no_locale;
  515.     /*
  516.     ** If the locale name is the same as our cache, use the cache.
  517.     */
  518.     lbuf = locale_buf;
  519.     if (lbuf != NULL && strcmp(name, lbuf) == 0) {
  520.         p = lbuf;
  521.         for (ap = (const char **) &localebuf;
  522.             ap < (const char **) (&localebuf + 1);
  523.                 ++ap)
  524.                     *ap = p += strlen(p) + 1;
  525.         return &localebuf;
  526.     }
  527.     /*
  528.     ** Slurp the locale file into the cache.
  529.     */
  530.     namesize = strlen(name) + 1;
  531.     if (sizeof(filename) <
  532.         sizeof(locale_home) + namesize + sizeof(lc_time))
  533.             goto no_locale;
  534.     oldsun = 0;
  535.     (void) sprintf(filename, "%s/%s/%s", locale_home, name, lc_time);
  536.     fd = open(filename, O_RDONLY);
  537.     if (fd < 0) {
  538.         /*
  539.         ** Old Sun systems have a different naming and data convention.
  540.         */
  541.         oldsun = 1;
  542.         (void) sprintf(filename, "%s/%s/%s", locale_home,
  543.             lc_time, name);
  544.         fd = open(filename, O_RDONLY);
  545.         if (fd < 0)
  546.             goto no_locale;
  547.     }
  548.     if (fstat(fd, &st) != 0)
  549.         goto bad_locale;
  550.     if (st.st_size <= 0)
  551.         goto bad_locale;
  552.     bufsize = namesize + st.st_size;
  553.     locale_buf = NULL;
  554.     lbuf = (lbuf == NULL || lbuf == locale_buf_C) ?
  555.         malloc(bufsize) : realloc(lbuf, bufsize);
  556.     if (lbuf == NULL)
  557.         goto bad_locale;
  558.     (void) strcpy(lbuf, name);
  559.     p = lbuf + namesize;
  560.     plim = p + st.st_size;
  561.     if (read(fd, p, (size_t) st.st_size) != st.st_size)
  562.         goto bad_lbuf;
  563.     if (close(fd) != 0)
  564.         goto bad_lbuf;
  565.     /*
  566.     ** Parse the locale file into localebuf.
  567.     */
  568.     if (plim[-1] != '\n')
  569.         goto bad_lbuf;
  570.     for (ap = (const char **) &localebuf;
  571.         ap < (const char **) (&localebuf + 1);
  572.             ++ap) {
  573.                 if (p == plim)
  574.                     goto bad_lbuf;
  575.                 *ap = p;
  576.                 while (*p != '\n')
  577.                     ++p;
  578.                 *p++ = '\0';
  579.     }
  580.     if (oldsun) {
  581.         /*
  582.         ** SunOS 4 used an obsolescent format; see localdtconv(3).
  583.         ** c_fmt had the ``short format for dates and times together''
  584.         ** (SunOS 4 date, "%a %b %e %T %Z %Y" in the C locale);
  585.         ** date_fmt had the ``long format for dates''
  586.         ** (SunOS 4 strftime %C, "%A, %B %e, %Y" in the C locale).
  587.         ** Discard the latter in favor of the former.
  588.         */
  589.         localebuf.date_fmt = localebuf.c_fmt;
  590.     }
  591.     /*
  592.     ** Record the successful parse in the cache.
  593.     */
  594.     locale_buf = lbuf;
  595.  
  596.     return &localebuf;
  597.  
  598. bad_lbuf:
  599.     free(lbuf);
  600. bad_locale:
  601.     (void) close(fd);
  602. no_locale:
  603.     localebuf = C_time_locale;
  604.     locale_buf = locale_buf_C;
  605.     return &localebuf;
  606. }
  607. #endif /* defined LOCALE_HOME */
  608.