home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / elm / elm2.4 / lib / strftime.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-05-08  |  13.7 KB  |  609 lines

  1.  
  2. static char rcsid[] = "@(#)$Id: strftime.c,v 5.4 1993/05/08 19:56:45 syd Exp $";
  3.  
  4. /*******************************************************************************
  5.  *  The Elm Mail System  -  $Revision: 5.4 $   $State: Exp $
  6.  *
  7.  * Public-domain relatively quick-and-dirty implemenation of
  8.  * ANSI library routine for System V Unix systems.
  9.  *
  10.  * Arnold Robbins
  11.  *
  12.  *******************************************************************************
  13.  * Bug reports, patches, comments, suggestions should be sent to:
  14.  * (Note: this routine is provided as is, without support for those sites that
  15.  *    do not have strftime in their library)
  16.  *
  17.  *    Syd Weinstein, Elm Coordinator
  18.  *    elm@DSI.COM            dsinc!elm
  19.  *
  20.  *******************************************************************************
  21.  * $Log: strftime.c,v $
  22.  * Revision 5.4  1993/05/08  19:56:45  syd
  23.  * update to newer version
  24.  * From: Syd
  25.  *
  26.  * Revision 5.3  1993/04/21  01:42:23  syd
  27.  * avoid name conflicts on min and max
  28.  *
  29.  * Revision 5.2  1993/04/16  04:29:34  syd
  30.  * attempt to bsdize a bit strftime
  31.  * From: many via syd
  32.  *
  33.  * Revision 5.1  1993/01/27  18:52:15  syd
  34.  * Initial checkin of contributed public domain routine.
  35.  * This routine is provided as is and not covered by Elm Copyright.
  36.  *
  37.  *
  38.  *
  39.  ******************************************************************************/
  40.  
  41. /*
  42.  * strftime.c
  43.  *
  44.  * Public-domain relatively quick-and-dirty implementation of
  45.  * ANSI library routine for System V Unix systems.
  46.  *
  47.  * It's written in old-style C for maximal portability.
  48.  * However, since I'm used to prototypes, I've included them too.
  49.  *
  50.  * If you want stuff in the System V ascftime routine, add the SYSV_EXT define.
  51.  * For extensions from SunOS, add SUNOS_EXT.
  52.  * For stuff needed to implement the P1003.2 date command, add POSIX2_DATE.
  53.  * For complete POSIX semantics, add POSIX_SEMANTICS.
  54.  *
  55.  * The code for %c, %x, and %X is my best guess as to what's "appropriate".
  56.  * This version ignores LOCALE information.
  57.  * It also doesn't worry about multi-byte characters.
  58.  * So there.
  59.  *
  60.  * This file is also shipped with GAWK (GNU Awk), gawk specific bits of
  61.  * code are included if GAWK is defined.
  62.  *
  63.  * Arnold Robbins
  64.  * January, February, March, 1991
  65.  * Updated March, April 1992
  66.  * Updated May, 1993
  67.  *
  68.  * Fixes from ado@elsie.nci.nih.gov
  69.  * February 1991, May 1992
  70.  * Fixes from Tor Lillqvist tor@tik.vtt.fi
  71.  * May, 1993
  72.  */
  73.  
  74. #include "headers.h"
  75.  
  76. #ifdef I_TIME
  77. #  include <time.h>
  78. #endif
  79. #ifdef I_SYSTIME
  80. #  include <sys/time.h>
  81. #endif
  82. #ifdef BSD
  83. #  include <sys/timeb.h>
  84. #endif
  85.  
  86. #include <ctype.h>
  87.  
  88. #ifndef __STDC__
  89. #define const    /**/
  90.  
  91. #ifndef BSD
  92. extern void tzset();
  93. #endif
  94. static int weeknumber();
  95. #else /* __STDC__ */
  96. #ifndef BSD
  97. extern void tzset(void);
  98. #endif
  99. static int weeknumber(const struct tm *timeptr, int firstweekday);
  100. #endif
  101.  
  102. /* defaults: season to taste */
  103. #define SYSV_EXT    1    /* stuff in System V ascftime routine */
  104. #define SUNOS_EXT    1    /* stuff in SunOS strftime routine */
  105. #define POSIX2_DATE    1    /* stuff in Posix 1003.2 date command */
  106. #define VMS_EXT        1    /* include %v for VMS date format */
  107. #define POSIX_SEMANTICS    1    /* call tzset() if TZ changes */
  108.  
  109. #if defined(POSIX2_DATE)
  110. #if ! defined(SYSV_EXT)
  111. #define SYSV_EXT    1
  112. #endif
  113. #if ! defined(SUNOS_EXT)
  114. #define SUNOS_EXT    1
  115. #endif
  116. #endif
  117.  
  118. #if defined(POSIX2_DATE)
  119. #define adddecl(stuff)    stuff
  120. #else
  121. #define adddecl(stuff)
  122. #endif
  123.  
  124. #undef strchr    /* avoid AIX weirdness */
  125.  
  126. adddecl(static int iso8601wknum();)
  127.  
  128. #ifdef __GNUC__
  129. #define inline    __inline__
  130. #else
  131. #define inline    /**/
  132. #endif
  133.  
  134. #define range(low, item, hi)    maximum(low, minimum(item, hi))
  135.  
  136. #ifndef BSD
  137. extern char *tzname[2];
  138. extern int daylight;
  139. #endif
  140.  
  141. /* minimum --- return minimum of two numbers */
  142.  
  143. #ifndef __STDC__
  144. static inline int
  145. minimum(a, b)
  146. int a, b;
  147. #else
  148. static inline int
  149. minimum(int a, int b)
  150. #endif
  151. {
  152.     return (a < b ? a : b);
  153. }
  154.  
  155. /* maximum --- return maximum of two numbers */
  156.  
  157. #ifndef __STDC__
  158. static inline int
  159. maximum(a, b)
  160. int a, b;
  161. #else
  162. static inline int
  163. maximum(int a, int b)
  164. #endif
  165. {
  166.     return (a > b ? a : b);
  167. }
  168.  
  169. /* strftime --- produce formatted time */
  170.  
  171. #ifndef __STDC__
  172. size_t
  173. strftime(s, maxsize, format, timeptr)
  174. char *s;
  175. size_t maxsize;
  176. const char *format;
  177. const struct tm *timeptr;
  178. #else
  179. size_t
  180. strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr)
  181. #endif
  182. {
  183.     char *endp = s + maxsize;
  184.     char *start = s;
  185.     char tbuf[100];
  186.     int i;
  187.     static short first = 1;
  188. #ifdef POSIX_SEMANTICS
  189.     static char *savetz = NULL;
  190.     static int savetzlen = 0;
  191.     char *tz;
  192. #endif /* POSIX_SEMANTICS */
  193.  
  194.     /* various tables, useful in North America */
  195.     static char *days_a[] = {
  196.         "Sun", "Mon", "Tue", "Wed",
  197.         "Thu", "Fri", "Sat",
  198.     };
  199.     static char *days_l[] = {
  200.         "Sunday", "Monday", "Tuesday", "Wednesday",
  201.         "Thursday", "Friday", "Saturday",
  202.     };
  203.     static char *months_a[] = {
  204.         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  205.         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
  206.     };
  207.     static char *months_l[] = {
  208.         "January", "February", "March", "April",
  209.         "May", "June", "July", "August", "September",
  210.         "October", "November", "December",
  211.     };
  212.     static char *ampm[] = { "AM", "PM", };
  213.  
  214.     if (s == NULL || format == NULL || timeptr == NULL || maxsize == 0)
  215.         return 0;
  216.  
  217.     if (strchr(format, '%') == NULL && strlen(format) + 1 >= maxsize)
  218.         return 0;
  219.  
  220. #ifndef POSIX_SEMANTICS
  221.     if (first) {
  222.         tzset();
  223.         first = 0;
  224.     }
  225. #else    /* POSIX_SEMANTICS */
  226.     tz = getenv("TZ");
  227.     if (first) {
  228.         if (tz != NULL) {
  229.             int tzlen = strlen(tz);
  230.  
  231.             savetz = (char *) malloc(tzlen + 1);
  232.             if (savetz != NULL) {
  233.                 savetzlen = tzlen + 1;
  234.                 strcpy(savetz, tz);
  235.             }
  236.         }
  237.         tzset();
  238.         first = 0;
  239.     }
  240.     /* if we have a saved TZ, and it is different, recapture and reset */
  241.     if (tz && savetz && (tz[0] != savetz[0] || strcmp(tz, savetz) != 0)) {
  242.         i = strlen(tz) + 1;
  243.         if (i > savetzlen) {
  244.             savetz = (char *) realloc(savetz, i);
  245.             if (savetz) {
  246.                 savetzlen = i;
  247.                 strcpy(savetz, tz);
  248.             }
  249.         } else
  250.             strcpy(savetz, tz);
  251.         tzset();
  252.     }
  253. #endif    /* POSIX_SEMANTICS */
  254.  
  255.     for (; *format && s < endp - 1; format++) {
  256.         tbuf[0] = '\0';
  257.         if (*format != '%') {
  258.             *s++ = *format;
  259.             continue;
  260.         }
  261.     again:
  262.         switch (*++format) {
  263.         case '\0':
  264.             *s++ = '%';
  265.             goto out;
  266.  
  267.         case '%':
  268.             *s++ = '%';
  269.             continue;
  270.  
  271.         case 'a':    /* abbreviated weekday name */
  272.             if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6)
  273.                 strcpy(tbuf, "?");
  274.             else
  275.                 strcpy(tbuf, days_a[timeptr->tm_wday]);
  276.             break;
  277.  
  278.         case 'A':    /* full weekday name */
  279.             if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6)
  280.                 strcpy(tbuf, "?");
  281.             else
  282.                 strcpy(tbuf, days_l[timeptr->tm_wday]);
  283.             break;
  284.  
  285. #ifdef SYSV_EXT
  286.         case 'h':    /* abbreviated month name */
  287. #endif
  288.         case 'b':    /* abbreviated month name */
  289.             if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11)
  290.                 strcpy(tbuf, "?");
  291.             else
  292.                 strcpy(tbuf, months_a[timeptr->tm_mon]);
  293.             break;
  294.  
  295.         case 'B':    /* full month name */
  296.             if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11)
  297.                 strcpy(tbuf, "?");
  298.             else
  299.                 strcpy(tbuf, months_l[timeptr->tm_mon]);
  300.             break;
  301.  
  302.         case 'c':    /* appropriate date and time representation */
  303.             sprintf(tbuf, "%s %s %2d %02d:%02d:%02d %d",
  304.                 days_a[range(0, timeptr->tm_wday, 6)],
  305.                 months_a[range(0, timeptr->tm_mon, 11)],
  306.                 range(1, timeptr->tm_mday, 31),
  307.                 range(0, timeptr->tm_hour, 23),
  308.                 range(0, timeptr->tm_min, 59),
  309.                 range(0, timeptr->tm_sec, 61),
  310.                 timeptr->tm_year + 1900);
  311.             break;
  312.  
  313.         case 'd':    /* day of the month, 01 - 31 */
  314.             i = range(1, timeptr->tm_mday, 31);
  315.             sprintf(tbuf, "%02d", i);
  316.             break;
  317.  
  318.         case 'H':    /* hour, 24-hour clock, 00 - 23 */
  319.             i = range(0, timeptr->tm_hour, 23);
  320.             sprintf(tbuf, "%02d", i);
  321.             break;
  322.  
  323.         case 'I':    /* hour, 12-hour clock, 01 - 12 */
  324.             i = range(0, timeptr->tm_hour, 23);
  325.             if (i == 0)
  326.                 i = 12;
  327.             else if (i > 12)
  328.                 i -= 12;
  329.             sprintf(tbuf, "%02d", i);
  330.             break;
  331.  
  332.         case 'j':    /* day of the year, 001 - 366 */
  333.             sprintf(tbuf, "%03d", timeptr->tm_yday + 1);
  334.             break;
  335.  
  336.         case 'm':    /* month, 01 - 12 */
  337.             i = range(0, timeptr->tm_mon, 11);
  338.             sprintf(tbuf, "%02d", i + 1);
  339.             break;
  340.  
  341.         case 'M':    /* minute, 00 - 59 */
  342.             i = range(0, timeptr->tm_min, 59);
  343.             sprintf(tbuf, "%02d", i);
  344.             break;
  345.  
  346.         case 'p':    /* am or pm based on 12-hour clock */
  347.             i = range(0, timeptr->tm_hour, 23);
  348.             if (i < 12)
  349.                 strcpy(tbuf, ampm[0]);
  350.             else
  351.                 strcpy(tbuf, ampm[1]);
  352.             break;
  353.  
  354.         case 'S':    /* second, 00 - 61 */
  355.             i = range(0, timeptr->tm_sec, 61);
  356.             sprintf(tbuf, "%02d", i);
  357.             break;
  358.  
  359.         case 'U':    /* week of year, Sunday is first day of week */
  360.             sprintf(tbuf, "%d", weeknumber(timeptr, 0));
  361.             break;
  362.  
  363.         case 'w':    /* weekday, Sunday == 0, 0 - 6 */
  364.             i = range(0, timeptr->tm_wday, 6);
  365.             sprintf(tbuf, "%d", i);
  366.             break;
  367.  
  368.         case 'W':    /* week of year, Monday is first day of week */
  369.             sprintf(tbuf, "%d", weeknumber(timeptr, 1));
  370.             break;
  371.  
  372.         case 'x':    /* appropriate date representation */
  373.             sprintf(tbuf, "%s %s %2d %d",
  374.                 days_a[range(0, timeptr->tm_wday, 6)],
  375.                 months_a[range(0, timeptr->tm_mon, 11)],
  376.                 range(1, timeptr->tm_mday, 31),
  377.                 timeptr->tm_year + 1900);
  378.             break;
  379.  
  380.         case 'X':    /* appropriate time representation */
  381.             sprintf(tbuf, "%02d:%02d:%02d",
  382.                 range(0, timeptr->tm_hour, 23),
  383.                 range(0, timeptr->tm_min, 59),
  384.                 range(0, timeptr->tm_sec, 61));
  385.             break;
  386.  
  387.         case 'y':    /* year without a century, 00 - 99 */
  388.             i = timeptr->tm_year % 100;
  389.             sprintf(tbuf, "%d", i);
  390.             break;
  391.  
  392.         case 'Y':    /* year with century */
  393.             sprintf(tbuf, "%d", 1900 + timeptr->tm_year);
  394.             break;
  395.  
  396.         case 'Z':    /* time zone name or abbrevation */
  397.             i = 0;
  398.             if (
  399. #ifndef TZNAME_MISSING
  400.                 daylight &&
  401. #endif
  402.                 timeptr->tm_isdst)
  403.                 i = 1;
  404. #ifdef TZNAME_MISSING
  405.             strcpy(tbuf, timeptr->tm_zone);
  406. #else
  407.             strcpy(tbuf, tzname[i]);
  408. #endif
  409.             break;
  410.  
  411. #ifdef SYSV_EXT
  412.         case 'n':    /* same as \n */
  413.             tbuf[0] = '\n';
  414.             tbuf[1] = '\0';
  415.             break;
  416.  
  417.         case 't':    /* same as \t */
  418.             tbuf[0] = '\t';
  419.             tbuf[1] = '\0';
  420.             break;
  421.  
  422.         case 'D':    /* date as %m/%d/%y */
  423.             strftime(tbuf, sizeof tbuf, "%m/%d/%y", timeptr);
  424.             break;
  425.  
  426.         case 'e':    /* day of month, blank padded */
  427.             sprintf(tbuf, "%2d", range(1, timeptr->tm_mday, 31));
  428.             break;
  429.  
  430.         case 'r':    /* time as %I:%M:%S %p */
  431.             strftime(tbuf, sizeof tbuf, "%I:%M:%S %p", timeptr);
  432.             break;
  433.  
  434.         case 'R':    /* time as %H:%M */
  435.             strftime(tbuf, sizeof tbuf, "%H:%M", timeptr);
  436.             break;
  437.  
  438.         case 'T':    /* time as %H:%M:%S */
  439.             strftime(tbuf, sizeof tbuf, "%H:%M:%S", timeptr);
  440.             break;
  441. #endif
  442.  
  443. #ifdef SUNOS_EXT
  444.         case 'k':    /* hour, 24-hour clock, blank pad */
  445.             sprintf(tbuf, "%2d", range(0, timeptr->tm_hour, 23));
  446.             break;
  447.  
  448.         case 'l':    /* hour, 12-hour clock, 1 - 12, blank pad */
  449.             i = range(0, timeptr->tm_hour, 23);
  450.             if (i == 0)
  451.                 i = 12;
  452.             else if (i > 12)
  453.                 i -= 12;
  454.             sprintf(tbuf, "%2d", i);
  455.             break;
  456. #endif
  457.  
  458.  
  459. #ifdef VMS_EXT
  460.         case 'v':    /* date as dd-bbb-YYYY */
  461.             sprintf(tbuf, "%2d-%3.3s-%4d",
  462.                 range(1, timeptr->tm_mday, 31),
  463.                 months_a[range(0, timeptr->tm_mon, 11)],
  464.                 timeptr->tm_year + 1900);
  465.             for (i = 3; i < 6; i++)
  466.                 if (islower(tbuf[i]))
  467.                     tbuf[i] = toupper(tbuf[i]);
  468.             break;
  469. #endif
  470.  
  471.  
  472. #ifdef POSIX2_DATE
  473.         case 'C':
  474.             sprintf(tbuf, "%02d", (timeptr->tm_year + 1900) / 100);
  475.             break;
  476.  
  477.  
  478.         case 'E':
  479.         case 'O':
  480.             /* POSIX locale extensions, ignored for now */
  481.             goto again;
  482.  
  483.         case 'V':    /* week of year according ISO 8601 */
  484. #if defined(GAWK) && defined(VMS_EXT)
  485.         {
  486.             extern int do_lint;
  487.             extern void warning();
  488.             static int warned = 0;
  489.  
  490.             if (! warned && do_lint) {
  491.                 warned = 1;
  492.                 warning(
  493.     "conversion %%V added in P1003.2/11.3; for VMS style date, use %%v");
  494.             }
  495.         }
  496. #endif
  497.             sprintf(tbuf, "%d", iso8601wknum(timeptr));
  498.             break;
  499.  
  500.         case 'u':
  501.         /* ISO 8601: Weekday as a decimal number [1 (Monday) - 7] */
  502.             sprintf(tbuf, "%d", timeptr->tm_wday == 0 ? 7 :
  503.                     timeptr->tm_wday);
  504.             break;
  505. #endif    /* POSIX2_DATE */
  506.         default:
  507.             tbuf[0] = '%';
  508.             tbuf[1] = *format;
  509.             tbuf[2] = '\0';
  510.             break;
  511.         }
  512.         i = strlen(tbuf);
  513.         if (i)
  514.             if (s + i < endp - 1) {
  515.                 strcpy(s, tbuf);
  516.                 s += i;
  517.             } else
  518.                 return 0;
  519.     }
  520. out:
  521.     if (s < endp && *format == '\0') {
  522.         *s = '\0';
  523.         return (s - start);
  524.     } else
  525.         return 0;
  526. }
  527.  
  528. #ifdef POSIX2_DATE
  529. /* iso8601wknum --- compute week number according to ISO 8601 */
  530.  
  531. #ifndef __STDC__
  532. static int
  533. iso8601wknum(timeptr)
  534. const struct tm *timeptr;
  535. #else
  536. static int
  537. iso8601wknum(const struct tm *timeptr)
  538. #endif
  539. {
  540.     /*
  541.      * From 1003.2 D11.3:
  542.      *    If the week (Monday to Sunday) containing January 1
  543.      *    has four or more days in the new year, then it is week 1;
  544.      *    otherwise it is week 53 of the previous year, and the
  545.      *    next week is week 1.
  546.      *
  547.      * ADR: This means if Jan 1 was Monday through Thursday,
  548.      *    it was week 1, otherwise week 53.
  549.      */
  550.  
  551.     int simple_wknum, jan1day, diff, ret;
  552.  
  553.     /* get week number, Monday as first day of the week */
  554.     simple_wknum = weeknumber(timeptr, 1) + 1;
  555.  
  556.     /*
  557.      * With thanks and tip of the hatlo to tml@tik.vtt.fi
  558.      *
  559.      * What day of the week does January 1 fall on?
  560.      * We know that
  561.      *    (timeptr->tm_yday - jan1.tm_yday) MOD 7 ==
  562.      *        (timeptr->tm_wday - jan1.tm_wday) MOD 7
  563.      * and that
  564.      *     jan1.tm_yday == 0
  565.      * and that
  566.      *     timeptr->tm_wday MOD 7 == timeptr->tm_wday
  567.      * from which it follows that. . .
  568.       */
  569.     jan1day = timeptr->tm_wday - (timeptr->tm_yday % 7);
  570.     if (jan1day < 0)
  571.         jan1day += 7;
  572.  
  573.     /*
  574.      * If Jan 1 was a Monday through Thursday, it was in
  575.      * week 1.  Otherwise it was last year's week 53, which is
  576.      * this year's week 0.
  577.      */
  578.     if (jan1day >= 1 && jan1day <= 4)
  579.         diff = 0;
  580.     else
  581.         diff = 1;
  582.     ret = simple_wknum - diff;
  583.     if (ret == 0)    /* we're in the first week of the year */
  584.         ret = 53;
  585.     return ret;
  586. }
  587. #endif
  588.  
  589. /* weeknumber --- figure how many weeks into the year */
  590.  
  591. /* With thanks and tip of the hatlo to ado@elsie.nci.nih.gov */
  592.  
  593. #ifndef __STDC__
  594. static int
  595. weeknumber(timeptr, firstweekday)
  596. const struct tm *timeptr;
  597. int firstweekday;
  598. #else
  599. static int
  600. weeknumber(const struct tm *timeptr, int firstweekday)
  601. #endif
  602. {
  603.     if (firstweekday == 0)
  604.         return (timeptr->tm_yday + 7 - timeptr->tm_wday) / 7;
  605.     else
  606.         return (timeptr->tm_yday + 7 -
  607.             (timeptr->tm_wday ? (timeptr->tm_wday - 1) : 6)) / 7;
  608. }
  609.