home *** CD-ROM | disk | FTP | other *** search
/ Chip 1995 March / CHIP3.mdf / slackwar / a / util / util-lin.2 / util-lin / util-linux-2.2 / time / strftime.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-02-22  |  13.6 KB  |  577 lines

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