]> sourceware.org Git - newlib-cygwin.git/commitdiff
* libc/time/strftime.c: Adapt for dual-purpose use so not only
authorCorinna Vinschen <corinna@vinschen.de>
Mon, 9 Mar 2009 12:35:03 +0000 (12:35 +0000)
committerCorinna Vinschen <corinna@vinschen.de>
Mon, 9 Mar 2009 12:35:03 +0000 (12:35 +0000)
defines strftime(), but can also define wcsftime(); add optional
test package; speed up %Y handling.
* libc/time/wcsftime.c:  New file, defining wcsftime() (albeit
indirectly by including strftime.c) and its documentation.
* libc/time/time.tex:  Enhance tm_isdst explanation, change strftime
description to match modified description in strftime.c
* libc/time/Makefile.am:  Add wcsftime.c and wcsftime.def.  Add a rule
so that wcsftime.o gets rebuilt when strftime changes.
* libc/time/Makefile.in:  Regenerate.
* libc/include/wchar.h (wcsftime): Declare.

newlib/ChangeLog
newlib/libc/include/wchar.h
newlib/libc/time/Makefile.am
newlib/libc/time/Makefile.in
newlib/libc/time/strftime.c
newlib/libc/time/time.tex
newlib/libc/time/wcsftime.c [new file with mode: 0644]

index c5b942fdaf1fcc55696ffed266878980843e1bec..2fb6bf7399535a7a7ec6a9b50f7fbcb27e58fc66 100644 (file)
@@ -1,3 +1,17 @@
+2009-03-09  Craig Howland <howland@LGSInnovations.com>
+
+       * libc/time/strftime.c:  Adapt for dual-purpose use so not only
+       defines strftime(), but can also define wcsftime(); add optional
+       test package; speed up %Y handling.
+       * libc/time/wcsftime.c:  New file, defining wcsftime() (albeit
+       indirectly by including strftime.c) and its documentation.
+       * libc/time/time.tex:  Enhance tm_isdst explanation, change strftime
+       description to match modified description in strftime.c
+       * libc/time/Makefile.am:  Add wcsftime.c and wcsftime.def.  Add a rule
+       so that wcsftime.o gets rebuilt when strftime changes.
+       * libc/time/Makefile.in:  Regenerate.
+       * libc/include/wchar.h (wcsftime): Declare.
+
 2009-03-06  Hans-Erik Floryd <hans-erik.floryd@rt-labs.com>,
            Corinna Vinschen  <corinna@vinschen.de>
 
index 37529a97fec4eb36d918a23bf070e4cf8cebce96..bce08bac0707e1823c45fd12e069c2f6d2169243 100644 (file)
@@ -72,6 +72,7 @@ int   _EXFUN(wcscoll, (const wchar_t *, const wchar_t *));
 wchar_t        *_EXFUN(wcscpy, (wchar_t * , const wchar_t *));
 wchar_t        *_EXFUN(wcpcpy, (wchar_t * , const wchar_t *));
 size_t _EXFUN(wcscspn, (const wchar_t *, const wchar_t *));
+size_t  _EXFUN(wcsftime, (wchar_t *, size_t, const wchar_t *, const struct tm *));
 size_t _EXFUN(wcslcat, (wchar_t *, const wchar_t *, size_t));
 size_t _EXFUN(wcslcpy, (wchar_t *, const wchar_t *, size_t));
 size_t _EXFUN(wcslen, (const wchar_t *));
index 7236a0a04409c514b4904b69fb103000ad7cf15b..95716315b22a6c33f6584f6a4b9cf055d03293c0 100644 (file)
@@ -24,7 +24,8 @@ LIB_SOURCES = \
        tzlock.c        \
        tzset.c         \
        tzset_r.c       \
-       tzvars.c
+       tzvars.c        \
+       wcsftime.c
 
 libtime_la_LDFLAGS = -Xcompiler -nostdlib
 
@@ -39,8 +40,11 @@ lib_a_CFLAGS = $(AM_CFLAGS)
 noinst_DATA =
 endif # USE_LIBTOOL
 
+# This rule is needed so that wcsftime.o is rebuilt when strftime.c changes.
 include $(srcdir)/../../Makefile.shared
 
+$(lpfx)wcsftime.$(oext): strftime.c
+
 CHEWOUT_FILES = \
        asctime.def     \
        clock.def       \
@@ -52,7 +56,8 @@ CHEWOUT_FILES = \
        strftime.def    \
        time.def        \
        tzlock.def      \
-       tzset.def
+       tzset.def       \
+       wcsftime.def
 
 SUFFIXES = .def
 
index 12d6175272e1261b7c9312e0da0e512b38d1498a..f330f853473cdb72a7cceda158d5b59ac21f9e8e 100644 (file)
@@ -66,7 +66,8 @@ am__objects_1 = lib_a-asctime.$(OBJEXT) lib_a-asctime_r.$(OBJEXT) \
        lib_a-mktm_r.$(OBJEXT) lib_a-strftime.$(OBJEXT) \
        lib_a-strptime.$(OBJEXT) lib_a-time.$(OBJEXT) \
        lib_a-tzlock.$(OBJEXT) lib_a-tzset.$(OBJEXT) \
-       lib_a-tzset_r.$(OBJEXT) lib_a-tzvars.$(OBJEXT)
+       lib_a-tzset_r.$(OBJEXT) lib_a-tzvars.$(OBJEXT) \
+       lib_a-wcsftime.$(OBJEXT)
 @USE_LIBTOOL_FALSE@am_lib_a_OBJECTS = $(am__objects_1)
 lib_a_OBJECTS = $(am_lib_a_OBJECTS)
 LTLIBRARIES = $(noinst_LTLIBRARIES)
@@ -74,7 +75,7 @@ libtime_la_LIBADD =
 am__objects_2 = asctime.lo asctime_r.lo clock.lo ctime.lo ctime_r.lo \
        difftime.lo gettzinfo.lo gmtime.lo gmtime_r.lo lcltime.lo \
        lcltime_r.lo mktime.lo mktm_r.lo strftime.lo strptime.lo \
-       time.lo tzlock.lo tzset.lo tzset_r.lo tzvars.lo
+       time.lo tzlock.lo tzset.lo tzset_r.lo tzvars.lo wcsftime.lo
 @USE_LIBTOOL_TRUE@am_libtime_la_OBJECTS = $(am__objects_2)
 libtime_la_OBJECTS = $(am_libtime_la_OBJECTS)
 @USE_LIBTOOL_TRUE@am_libtime_la_rpath =
@@ -283,7 +284,8 @@ LIB_SOURCES = \
        tzlock.c        \
        tzset.c         \
        tzset_r.c       \
-       tzvars.c
+       tzvars.c        \
+       wcsftime.c
 
 libtime_la_LDFLAGS = -Xcompiler -nostdlib
 @USE_LIBTOOL_TRUE@noinst_LTLIBRARIES = libtime.la
@@ -304,7 +306,8 @@ CHEWOUT_FILES = \
        strftime.def    \
        time.def        \
        tzlock.def      \
-       tzset.def
+       tzset.def       \
+       wcsftime.def
 
 SUFFIXES = .def
 CHEW = ../../doc/makedoc -f $(srcdir)/../../doc/doc.str
@@ -497,6 +500,12 @@ lib_a-tzvars.o: tzvars.c
 lib_a-tzvars.obj: tzvars.c
        $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-tzvars.obj `if test -f 'tzvars.c'; then $(CYGPATH_W) 'tzvars.c'; else $(CYGPATH_W) '$(srcdir)/tzvars.c'; fi`
 
+lib_a-wcsftime.o: wcsftime.c
+       $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-wcsftime.o `test -f 'wcsftime.c' || echo '$(srcdir)/'`wcsftime.c
+
+lib_a-wcsftime.obj: wcsftime.c
+       $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-wcsftime.obj `if test -f 'wcsftime.c'; then $(CYGPATH_W) 'wcsftime.c'; else $(CYGPATH_W) '$(srcdir)/wcsftime.c'; fi`
+
 mostlyclean-libtool:
        -rm -f *.lo
 
@@ -651,6 +660,9 @@ objectlist.awk.in: $(noinst_LTLIBRARIES)
          echo $$i `pwd`/$$i >> objectlist.awk.in ; \
        done
 
+# This rule is needed so that wcsftime.o is rebuilt when strftime.c changes.
+$(lpfx)wcsftime.$(oext): strftime.c
+
 .c.def:
        $(CHEW) < $< > $*.def 2> $*.ref
        touch stmp-def
index a3fdb745ae27e053789a721847dee5eb7361933f..88ccf46b5cf51553f94cdd7ecdea69347d5dfb2a 100644 (file)
@@ -1,7 +1,11 @@
+/* NOTE:  This file defines both strftime() and wcsftime().  Take care when
+ * making changes.  See also wcsftime.c, and note the (small) overlap in the
+ * manual description, taking care to edit both as needed.  */
 /*
  * strftime.c
  * Original Author:    G. Haley
  * Additions from:     Eric Blake
+ * Changes to allow dual use as wcstime, also: Craig Howland
  *
  * Places characters into the array pointed to by s as controlled by the string
  * pointed to by format. If the total number of resulting characters including
@@ -13,7 +17,7 @@
 
 /*
 FUNCTION
-<<strftime>>---flexible calendar time formatter
+<<strftime>>---convert date and time to a formatted string
 
 INDEX
        strftime
@@ -128,11 +132,11 @@ The count of days in the year, formatted with three digits
 
 o %k
 The hour (on a 24-hour clock), formatted with leading space if single
-digit (from `<<0>>' to `<<23>>'). Non-POSIX extension. [tm_hour]
+digit (from `<<0>>' to `<<23>>'). Non-POSIX extension (c.p. %I). [tm_hour]
 
 o %l
 The hour (on a 12-hour clock), formatted with leading space if single
-digit (from `<<1>>' to `<<12>>'). Non-POSIX extension. [tm_hour]
+digit (from `<<1>>' to `<<12>>'). Non-POSIX extension (c.p. %H). [tm_hour]
 
 o %m
 The month number, formatted with two digits (from `<<01>>' to `<<12>>').
@@ -203,6 +207,7 @@ seconds), equivalent to "%H:%M:%S". [tm_sec, tm_min, tm_hour]
 
 o %y
 The last two digits of the year (from `<<00>>' to `<<99>>'). [tm_year]
+(Implementation interpretation:  always positive, even for negative years.)
 
 o %Y
 The full year, equivalent to <<%C%y>>.  It will always have at least four
@@ -253,22 +258,48 @@ This implementation does not support <<s>> being NULL, nor overlapping
 #include <time.h>
 #include <string.h>
 #include <stdlib.h>
+#include <limits.h>
 #include "local.h"
+/* Defines to make the file dual use for either strftime() or wcsftime().
+ * To get wcsftime, define MAKE_WCSFTIME.
+ * To get strftime, do not define MAKE_WCSFTIME.
+ * Names are kept friendly to strftime() usage.  The biggest ugliness is the
+ * use of the CQ() macro to make either regular character constants and
+ * string literals or wide-character constants and wide-character-string
+ * literals, as appropriate.  */
+#if !defined(MAKE_WCSFTIME)
+#  define CHAR         char            /* string type basis */
+#  define CQ(a)                a               /* character constant qualifier */
+#  define SFLG                         /* %s flag (null for normal char) */
+# else
+#  define strftime     wcsftime        /* Alternate function name */
+#  define CHAR         wchar_t         /* string type basis */
+#  define CQ(a)                L##a            /* character constant qualifier */
+#  define snprintf     swprintf        /* wide-char equivalent function name */
+#  define strncmp      wcsncmp         /* wide-char equivalent function name */
+#  define SFLG         "l"             /* %s flag (l for wide char) */
+#endif  /* MAKE_WCSFTIME */
+
+/* Enforce the coding assumptions that YEAR_BASE is positive.  (%C, %Y, etc.) */
+#if YEAR_BASE < 0
+#  error "YEAR_BASE < 0"
+#endif
 
 static _CONST int dname_len[7] =
 {6, 6, 7, 9, 8, 6, 8};
 
-static _CONST char *_CONST dname[7] =
-{"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"};
+static _CONST CHAR *_CONST dname[7] =
+{CQ("Sunday"), CQ("Monday"), CQ("Tuesday"), CQ("Wednesday"),
CQ("Thursday"), CQ("Friday"), CQ("Saturday")};
 
 static _CONST int mname_len[12] =
 {7, 8, 5, 5, 3, 4, 4, 6, 9, 7, 8, 8};
 
-static _CONST char *_CONST mname[12] =
-{"January", "February", "March", "April",
"May", "June", "July", "August", "September", "October", "November",
"December"};
+static _CONST CHAR *_CONST mname[12] =
+{CQ("January"), CQ("February"), CQ("March"), CQ("April"),
CQ("May"), CQ("June"), CQ("July"), CQ("August"),
CQ("September"), CQ("October"), CQ("November"), CQ("December")};
 
 /* Using the tm_year, tm_wday, and tm_yday components of TIM_P, return
    -1, 0, or 1 as the adjustment to add to the year for the ISO week
@@ -319,9 +350,9 @@ _DEFUN (iso_year_adjust, (tim_p),
 
 size_t
 _DEFUN (strftime, (s, maxsize, format, tim_p),
-       char *s _AND
+       CHAR *s _AND
        size_t maxsize _AND
-       _CONST char *format _AND
+       _CONST CHAR *format _AND
        _CONST struct tm *tim_p)
 {
   size_t count = 0;
@@ -329,7 +360,7 @@ _DEFUN (strftime, (s, maxsize, format, tim_p),
 
   for (;;)
     {
-      while (*format && *format != '%')
+      while (*format && *format != CQ('%'))
        {
          if (count < maxsize - 1)
            s[count++] = *format++;
@@ -337,16 +368,16 @@ _DEFUN (strftime, (s, maxsize, format, tim_p),
            return 0;
        }
 
-      if (*format == '\0')
+      if (*format == CQ('\0'))
        break;
 
       format++;
-      if (*format == 'E' || *format == 'O')
+      if (*format == CQ('E') || *format == CQ('O'))
        format++;
 
       switch (*format)
        {
-       case 'a':
+       case CQ('a'):
          for (i = 0; i < 3; i++)
            {
              if (count < maxsize - 1)
@@ -356,7 +387,7 @@ _DEFUN (strftime, (s, maxsize, format, tim_p),
                return 0;
            }
          break;
-       case 'A':
+       case CQ('A'):
          for (i = 0; i < dname_len[tim_p->tm_wday]; i++)
            {
              if (count < maxsize - 1)
@@ -366,8 +397,8 @@ _DEFUN (strftime, (s, maxsize, format, tim_p),
                return 0;
            }
          break;
-       case 'b':
-       case 'h':
+       case CQ('b'):
+       case CQ('h'):
          for (i = 0; i < 3; i++)
            {
              if (count < maxsize - 1)
@@ -377,7 +408,7 @@ _DEFUN (strftime, (s, maxsize, format, tim_p),
                return 0;
            }
          break;
-       case 'B':
+       case CQ('B'):
          for (i = 0; i < mname_len[tim_p->tm_mon]; i++)
            {
              if (count < maxsize - 1)
@@ -387,18 +418,18 @@ _DEFUN (strftime, (s, maxsize, format, tim_p),
                return 0;
            }
          break;
-       case 'c':
+       case CQ('c'):
          {
-           /* Length is not known because of %C%y, so recurse. */
+           /* Recurse to avoid need to replicate %Y formation. */
            size_t adjust = strftime (&s[count], maxsize - count,
-                                     "%a %b %e %H:%M:%S %C%y", tim_p);
+                                     CQ("%a %b %e %H:%M:%S %Y"), tim_p);
            if (adjust > 0)
              count += adjust;
            else
              return 0;
          }
          break;
-       case 'C':
+       case CQ('C'):
          {
            /* Examples of (tm_year + YEAR_BASE) that show how %Y == %C%y
               with 32-bit int.
@@ -424,68 +455,57 @@ _DEFUN (strftime, (s, maxsize, format, tim_p),
            int century = tim_p->tm_year >= 0
              ? tim_p->tm_year / 100 + YEAR_BASE / 100
              : abs (tim_p->tm_year + YEAR_BASE) / 100;
-            count += snprintf (&s[count], maxsize - count, "%s%.*d",
-                               neg ? "-" : "", 2 - neg, century);
+            count += snprintf (&s[count], maxsize - count, CQ("%s%.*d"),
+                               neg ? CQ("-") : CQ(""), 2 - neg, century);
             if (count >= maxsize)
               return 0;
          }
          break;
-       case 'd':
-       case 'e':
-         if (count < maxsize - 2)
-           {
-             sprintf (&s[count], *format == 'd' ? "%.2d" : "%2d",
-                      tim_p->tm_mday);
-             count += 2;
-           }
-         else
-           return 0;
+       case CQ('d'):
+       case CQ('e'):
+         count += snprintf (&s[count], maxsize - count,
+                       *format == CQ('d') ? CQ("%.2d") : CQ("%2d"),
+                       tim_p->tm_mday);
+         if (count >= maxsize)  return 0;
          break;
-       case 'D':
-       case 'x':
+       case CQ('D'):
+       case CQ('x'):
          /* %m/%d/%y */
-         if (count < maxsize - 8)
-           {
-             sprintf (&s[count], "%.2d/%.2d/%.2d",
-                      tim_p->tm_mon + 1, tim_p->tm_mday,
-                      tim_p->tm_year >= 0 ? tim_p->tm_year % 100
-                      : abs (tim_p->tm_year + YEAR_BASE) % 100);
-             count += 8;
-           }
-         else
-           return 0;
+         count += snprintf (&s[count], maxsize - count,
+                       CQ("%.2d/%.2d/%.2d"),
+                       tim_p->tm_mon + 1, tim_p->tm_mday,
+                       tim_p->tm_year >= 0 ? tim_p->tm_year % 100
+                       : abs (tim_p->tm_year + YEAR_BASE) % 100);
+          if (count >= maxsize)  return 0;
          break;
-        case 'F':
-         {
-           /* Length is not known because of %C%y, so recurse. */
+       case CQ('F'):
+         { /* %F is equivalent to "%Y-%m-%d" */
+           /* Recurse to avoid need to replicate %Y formation. */
            size_t adjust = strftime (&s[count], maxsize - count,
-                                     "%C%y-%m-%d", tim_p);
+                                     CQ("%Y-%m-%d"), tim_p);
            if (adjust > 0)
              count += adjust;
            else
              return 0;
          }
           break;
-        case 'g':
-         if (count < maxsize - 2)
-           {
-             /* Be careful of both overflow and negative years, thanks to
+       case CQ('g'):
+         /* Be careful of both overflow and negative years, thanks to
                 the asymmetric range of years.  */
-             int adjust = iso_year_adjust (tim_p);
-             int year = tim_p->tm_year >= 0 ? tim_p->tm_year % 100
+         {
+           int adjust = iso_year_adjust (tim_p);
+           int year = tim_p->tm_year >= 0 ? tim_p->tm_year % 100
                : abs (tim_p->tm_year + YEAR_BASE) % 100;
-             if (adjust < 0 && tim_p->tm_year <= -YEAR_BASE)
+           if (adjust < 0 && tim_p->tm_year <= -YEAR_BASE)
                adjust = 1;
-             else if (adjust > 0 && tim_p->tm_year < -YEAR_BASE)
+           else if (adjust > 0 && tim_p->tm_year < -YEAR_BASE)
                adjust = -1;
-             sprintf (&s[count], "%.2d",
+           count += snprintf (&s[count], maxsize - count, CQ("%.2d"),
                       ((year + adjust) % 100 + 100) % 100);
-             count += 2;
-           }
-         else
-           return 0;
+            if (count >= maxsize)  return 0;
+         }
           break;
-        case 'G':
+       case CQ('G'):
          {
            /* See the comments for 'C' and 'Y'; this is a variable length
               field.  Although there is no requirement for a minimum number
@@ -512,192 +532,128 @@ _DEFUN (strftime, (s, maxsize, format, tim_p),
                year = 0;
                ++century;
              }
-            count += snprintf (&s[count], maxsize - count, "%s%.*d%.2d",
-                               neg ? "-" : "", 2 - neg, century, year);
+            count += snprintf (&s[count], maxsize - count, CQ("%s%.*d%.2d"),
+                               neg ? CQ("-") : CQ(""), 2 - neg, century, year);
             if (count >= maxsize)
               return 0;
          }
           break;
-       case 'H':
-       case 'k':
-         if (count < maxsize - 2)
-           {
-             sprintf (&s[count], *format == 'k' ? "%2d" : "%.2d",
-                      tim_p->tm_hour);
-             count += 2;
-           }
-         else
-           return 0;
+       case CQ('H'):
+       case CQ('k'):   /* newlib extension */
+         count += snprintf (&s[count], maxsize - count,
+                       *format == CQ('k') ? CQ("%2d") : CQ("%.2d"),
+                       tim_p->tm_hour);
+          if (count >= maxsize)  return 0;
          break;
-       case 'I':
-       case 'l':
-         if (count < maxsize - 2)
-           {
-             if (tim_p->tm_hour == 0 ||
-                 tim_p->tm_hour == 12)
-               {
-                 s[count++] = '1';
-                 s[count++] = '2';
-               }
-             else
-               {
-                 sprintf (&s[count], *format == 'I' ? "%.2d" : "%2d",
-                          tim_p->tm_hour % 12);
-                 count += 2;
-               }
-           }
-         else
-           return 0;
+       case CQ('I'):
+       case CQ('l'):   /* newlib extension */
+         {
+           register int  h12;
+           h12 = (tim_p->tm_hour == 0 || tim_p->tm_hour == 12)  ?
+                                               12  :  tim_p->tm_hour % 12;
+           count += snprintf (&s[count], maxsize - count,
+                       *format == CQ('I') ? CQ("%.2d") : CQ("%2d"),
+                       h12);
+           if (count >= maxsize)  return 0;
+         }
          break;
-       case 'j':
-         if (count < maxsize - 3)
-           {
-             sprintf (&s[count], "%.3d",
-                      tim_p->tm_yday + 1);
-             count += 3;
-           }
-         else
-           return 0;
+       case CQ('j'):
+         count += snprintf (&s[count], maxsize - count, CQ("%.3d"),
+                       tim_p->tm_yday + 1);
+          if (count >= maxsize)  return 0;
          break;
-       case 'm':
-         if (count < maxsize - 2)
-           {
-             sprintf (&s[count], "%.2d",
-                      tim_p->tm_mon + 1);
-             count += 2;
-           }
-         else
-           return 0;
+       case CQ('m'):
+         count += snprintf (&s[count], maxsize - count, CQ("%.2d"),
+                       tim_p->tm_mon + 1);
+          if (count >= maxsize)  return 0;
          break;
-       case 'M':
-         if (count < maxsize - 2)
-           {
-             sprintf (&s[count], "%.2d",
-                      tim_p->tm_min);
-             count += 2;
-           }
-         else
-           return 0;
+       case CQ('M'):
+         count += snprintf (&s[count], maxsize - count, CQ("%.2d"),
+                       tim_p->tm_min);
+          if (count >= maxsize)  return 0;
          break;
-       case 'n':
+       case CQ('n'):
          if (count < maxsize - 1)
-           s[count++] = '\n';
+           s[count++] = CQ('\n');
          else
            return 0;
          break;
-       case 'p':
-         if (count < maxsize - 2)
+       case CQ('p'):
+         if (count < maxsize - 1)
            {
              if (tim_p->tm_hour < 12)
-               s[count++] = 'A';
+               s[count++] = CQ('A');
              else
-               s[count++] = 'P';
-
-             s[count++] = 'M';
+               s[count++] = CQ('P');
            }
-         else
-           return 0;
-         break;
-       case 'r':
-         if (count < maxsize - 11)
+         if (count < maxsize - 1)
            {
-             if (tim_p->tm_hour == 0 ||
-                 tim_p->tm_hour == 12)
-               {
-                 s[count++] = '1';
-                 s[count++] = '2';
-               }
-             else
-               {
-                 sprintf (&s[count], "%.2d", tim_p->tm_hour % 12);
-                 count += 2;
-               }
-             s[count++] = ':';
-             sprintf (&s[count], "%.2d",
-                      tim_p->tm_min);
-             count += 2;
-             s[count++] = ':';
-             sprintf (&s[count], "%.2d",
-                      tim_p->tm_sec);
-             count += 2;
-             s[count++] = ' ';
-             if (tim_p->tm_hour < 12)
-               s[count++] = 'A';
-             else
-               s[count++] = 'P';
-
-             s[count++] = 'M';
+             s[count++] = CQ('M');
            }
          else
            return 0;
          break;
-        case 'R':
-          if (count < maxsize - 5)
-            {
-              sprintf (&s[count], "%.2d:%.2d", tim_p->tm_hour, tim_p->tm_min);
-              count += 5;
-            }
-          else
-            return 0;
+       case CQ('r'):
+         {
+           register int  h12;
+           h12 = (tim_p->tm_hour == 0 || tim_p->tm_hour == 12)  ?
+                                               12  :  tim_p->tm_hour % 12;
+           count += snprintf (&s[count], maxsize - count,
+                       CQ("%.2d:%.2d:%.2d %cM"),
+                       h12, 
+                       tim_p->tm_min,
+                       tim_p->tm_sec,
+                       (tim_p->tm_hour < 12)  ?  CQ('A') :  CQ('P'));
+           if (count >= maxsize)  return 0;
+         }
+         break;
+       case CQ('R'):
+          count += snprintf (&s[count], maxsize - count, CQ("%.2d:%.2d"),
+                       tim_p->tm_hour, tim_p->tm_min);
+          if (count >= maxsize)  return 0;
           break;
-       case 'S':
-         if (count < maxsize - 2)
-           {
-             sprintf (&s[count], "%.2d",
-                      tim_p->tm_sec);
-             count += 2;
-           }
-         else
-           return 0;
+       case CQ('S'):
+         count += snprintf (&s[count], maxsize - count, CQ("%.2d"),
+                       tim_p->tm_sec);
+          if (count >= maxsize)  return 0;
          break;
-       case 't':
+       case CQ('t'):
          if (count < maxsize - 1)
-           s[count++] = '\t';
+           s[count++] = CQ('\t');
          else
            return 0;
          break;
-        case 'T':
-        case 'X':
-          if (count < maxsize - 8)
-            {
-              sprintf (&s[count], "%.2d:%.2d:%.2d", tim_p->tm_hour,
-                       tim_p->tm_min, tim_p->tm_sec);
-              count += 8;
-            }
-          else
-            return 0;
+       case CQ('T'):
+       case CQ('X'):
+          count += snprintf (&s[count], maxsize - count, CQ("%.2d:%.2d:%.2d"),
+                       tim_p->tm_hour, tim_p->tm_min, tim_p->tm_sec);
+          if (count >= maxsize)  return 0;
           break;
-        case 'u':
+       case CQ('u'):
           if (count < maxsize - 1)
             {
               if (tim_p->tm_wday == 0)
-                s[count++] = '7';
+                s[count++] = CQ('7');
               else
-                s[count++] = '0' + tim_p->tm_wday;
+                s[count++] = CQ('0') + tim_p->tm_wday;
             }
           else
             return 0;
           break;
-       case 'U':
-         if (count < maxsize - 2)
-           {
-             sprintf (&s[count], "%.2d",
+       case CQ('U'):
+         count += snprintf (&s[count], maxsize - count, CQ("%.2d"),
                       (tim_p->tm_yday + 7 -
                        tim_p->tm_wday) / 7);
-             count += 2;
-           }
-         else
-           return 0;
+          if (count >= maxsize)  return 0;
          break;
-        case 'V':
-         if (count < maxsize - 2)
-           {
-             int adjust = iso_year_adjust (tim_p);
-             int wday = (tim_p->tm_wday) ? tim_p->tm_wday - 1 : 6;
-             int week = (tim_p->tm_yday + 10 - wday) / 7;
-             if (adjust > 0)
+       case CQ('V'):
+         {
+           int adjust = iso_year_adjust (tim_p);
+           int wday = (tim_p->tm_wday) ? tim_p->tm_wday - 1 : 6;
+           int week = (tim_p->tm_yday + 10 - wday) / 7;
+           if (adjust > 0)
                week = 1;
-             else if (adjust < 0)
+           else if (adjust < 0)
                /* Previous year has 53 weeks if current year starts on
                   Fri, and also if current year starts on Sat and
                   previous year was leap year.  */
@@ -706,75 +662,69 @@ _DEFUN (strftime, (s, maxsize, format, tim_p),
                                             + (YEAR_BASE - 1
                                                - (tim_p->tm_year < 0
                                                   ? 0 : 2000)))));
-             sprintf (&s[count], "%.2d", week);
-             count += 2;
-           }
-         else
-           return 0;
+           count += snprintf (&s[count], maxsize - count, CQ("%.2d"), week);
+            if (count >= maxsize)  return 0;
+         }
           break;
-       case 'w':
+       case CQ('w'):
          if (count < maxsize - 1)
-            s[count++] = '0' + tim_p->tm_wday;
+            s[count++] = CQ('0') + tim_p->tm_wday;
          else
            return 0;
          break;
-       case 'W':
-         if (count < maxsize - 2)
-           {
-             int wday = (tim_p->tm_wday) ? tim_p->tm_wday - 1 : 6;
-             sprintf (&s[count], "%.2d",
-                      (tim_p->tm_yday + 7 - wday) / 7);
-             count += 2;
-           }
-         else
-           return 0;
+       case CQ('W'):
+         {
+           int wday = (tim_p->tm_wday) ? tim_p->tm_wday - 1 : 6;
+           count += snprintf (&s[count], maxsize - count, CQ("%.2d"),
+                       (tim_p->tm_yday + 7 - wday) / 7);
+            if (count >= maxsize)  return 0;
+         }
          break;
-       case 'y':
-         if (count < maxsize - 2)
+       case CQ('y'):
            {
              /* Be careful of both overflow and negative years, thanks to
                 the asymmetric range of years.  */
              int year = tim_p->tm_year >= 0 ? tim_p->tm_year % 100
                : abs (tim_p->tm_year + YEAR_BASE) % 100;
-             sprintf (&s[count], "%.2d", year);
-             count += 2;
+             count += snprintf (&s[count], maxsize - count, CQ("%.2d"), year);
+              if (count >= maxsize)  return 0;
            }
-         else
-           return 0;
          break;
-       case 'Y':
-         {
-           /* Length is not known because of %C%y, so recurse. */
-           size_t adjust = strftime (&s[count], maxsize - count,
-                                     "%C%y", tim_p);
-           if (adjust > 0)
-             count += adjust;
-           else
-             return 0;
+       case CQ('Y'):
+         /* An implementation choice is to have %Y match %C%y, so that it
+          * gives at least 4 digits, with leading zeros as needed.  */
+         if(tim_p->tm_year <= INT_MAX-YEAR_BASE)  {
+           /* For normal, non-overflow case.  */
+           count += snprintf (&s[count], maxsize - count, CQ("%04d"),
+                               tim_p->tm_year + YEAR_BASE);
+         }
+         else  {
+           /* int would overflow, so use unsigned instead.  */
+           register unsigned year;
+           year = (unsigned) tim_p->tm_year + (unsigned) YEAR_BASE;
+           count += snprintf (&s[count], maxsize - count, CQ("%04u"),
+                               tim_p->tm_year + YEAR_BASE);
          }
+          if (count >= maxsize)  return 0;
          break;
-        case 'z':
+       case CQ('z'):
           if (tim_p->tm_isdst >= 0)
             {
-             if (count < maxsize - 5)
-               {
-                 long offset;
-                 __tzinfo_type *tz = __gettzinfo ();
-                 TZ_LOCK;
-                 /* The sign of this is exactly opposite the envvar TZ.  We
-                    could directly use the global _timezone for tm_isdst==0,
-                    but have to use __tzrule for daylight savings.  */
-                 offset = -tz->__tzrule[tim_p->tm_isdst > 0].offset;
-                 TZ_UNLOCK;
-                 sprintf (&s[count], "%+03ld%.2ld", offset / SECSPERHOUR,
-                          labs (offset / SECSPERMIN) % 60L);
-                 count += 5;
-               }
-             else
-               return 0;
+             long offset;
+             __tzinfo_type *tz = __gettzinfo ();
+             TZ_LOCK;
+             /* The sign of this is exactly opposite the envvar TZ.  We
+                could directly use the global _timezone for tm_isdst==0,
+                but have to use __tzrule for daylight savings.  */
+             offset = -tz->__tzrule[tim_p->tm_isdst > 0].offset;
+             TZ_UNLOCK;
+             count += snprintf (&s[count], maxsize - count, CQ("%+03ld%.2ld"),
+                       offset / SECSPERHOUR,
+                       labs (offset / SECSPERMIN) % 60L);
+              if (count >= maxsize)  return 0;
             }
           break;
-       case 'Z':
+       case CQ('Z'):
          if (tim_p->tm_isdst >= 0)
            {
              int size;
@@ -793,9 +743,9 @@ _DEFUN (strftime, (s, maxsize, format, tim_p),
              TZ_UNLOCK;
            }
          break;
-       case '%':
+       case CQ('%'):
          if (count < maxsize - 1)
-           s[count++] = '%';
+           s[count++] = CQ('%');
          else
            return 0;
          break;
@@ -806,7 +756,459 @@ _DEFUN (strftime, (s, maxsize, format, tim_p),
        break;
     }
   if (maxsize)
-    s[count] = '\0';
+    s[count] = CQ('\0');
 
   return count;
 }
+/* The remainder of this file can serve as a regression test.  Compile
+ *  with -D_REGRESSION_TEST.  */
+#if defined(_REGRESSION_TEST)  /* [Test code:  */
+/* This test code relies on ANSI C features, in particular on the ability
+ * of adjacent strings to be pasted together into one string.  */
+/* Test output buffer size (should be larger than all expected results) */
+#define OUTSIZE        256
+struct test {
+       CHAR  *fmt;     /* Testing format */
+       size_t  max;    /* Testing maxsize */
+       size_t  ret;    /* Expected return value */
+       CHAR  *out;     /* Expected output string */
+       };
+struct list {
+       const struct tm  *tms;  /* Time used for these vectors */
+       const struct test *vec; /* Test vectors */
+       int  cnt;               /* Number of vectors */
+       };
+const char  TZ[]="TZ=EST5EDT";
+/* Define list of test inputs and expected outputs, for the given time zone
+ * and time.  */
+const struct tm  tm0 = {
+       /* Tue Dec 30 10:53:47 EST 2008 (time_t=1230648827) */
+       .tm_sec         = 47,
+       .tm_min         = 53,
+       .tm_hour        = 9,
+       .tm_mday        = 30,
+       .tm_mon         = 11,
+       .tm_year        = 108,
+       .tm_wday        = 2,
+       .tm_yday        = 364,
+       .tm_isdst       = 0
+       };
+const struct test  Vec0[] = {
+       /* Testing fields one at a time, expecting to pass, using exact
+        * allowed length as what is needed.  */
+       /* Using tm0 for time: */
+       #define EXP(s)  sizeof(s)-1, s
+       { CQ("%a"), 3+1, EXP(CQ("Tue")) },
+       { CQ("%A"), 7+1, EXP(CQ("Tuesday")) },
+       { CQ("%b"), 3+1, EXP(CQ("Dec")) },
+       { CQ("%B"), 8+1, EXP(CQ("December")) },
+       { CQ("%c"), 24+1, EXP(CQ("Tue Dec 30 09:53:47 2008")) },
+       { CQ("%C"), 2+1, EXP(CQ("20")) },
+       { CQ("%d"), 2+1, EXP(CQ("30")) },
+       { CQ("%D"), 8+1, EXP(CQ("12/30/08")) },
+       { CQ("%e"), 2+1, EXP(CQ("30")) },
+       { CQ("%F"), 10+1, EXP(CQ("2008-12-30")) },
+       { CQ("%g"), 2+1, EXP(CQ("09")) },
+       { CQ("%G"), 4+1, EXP(CQ("2009")) },
+       { CQ("%h"), 3+1, EXP(CQ("Dec")) },
+       { CQ("%H"), 2+1, EXP(CQ("09")) },
+       { CQ("%I"), 2+1, EXP(CQ("09")) },
+       { CQ("%j"), 3+1, EXP(CQ("365")) },
+       { CQ("%k"), 2+1, EXP(CQ(" 9")) },
+       { CQ("%l"), 2+1, EXP(CQ(" 9")) },
+       { CQ("%m"), 2+1, EXP(CQ("12")) },
+       { CQ("%M"), 2+1, EXP(CQ("53")) },
+       { CQ("%n"), 1+1, EXP(CQ("\n")) },
+       { CQ("%p"), 2+1, EXP(CQ("AM")) },
+       { CQ("%r"), 11+1, EXP(CQ("09:53:47 AM")) },
+       { CQ("%R"), 5+1, EXP(CQ("09:53")) },
+       { CQ("%S"), 2+1, EXP(CQ("47")) },
+       { CQ("%t"), 1+1, EXP(CQ("\t")) },
+       { CQ("%T"), 8+1, EXP(CQ("09:53:47")) },
+       { CQ("%u"), 1+1, EXP(CQ("2")) },
+       { CQ("%U"), 2+1, EXP(CQ("52")) },
+       { CQ("%V"), 2+1, EXP(CQ("01")) },
+       { CQ("%w"), 1+1, EXP(CQ("2")) },
+       { CQ("%W"), 2+1, EXP(CQ("52")) },
+       { CQ("%x"), 8+1, EXP(CQ("12/30/08")) },
+       { CQ("%X"), 8+1, EXP(CQ("09:53:47")) },
+       { CQ("%y"), 2+1, EXP(CQ("08")) },
+       { CQ("%Y"), 4+1, EXP(CQ("2008")) },
+       { CQ("%z"), 5+1, EXP(CQ("-0500")) },
+       { CQ("%Z"), 3+1, EXP(CQ("EST")) },
+       { CQ("%%"), 1+1, EXP(CQ("%")) },
+       #undef EXP
+       };
+/* Define list of test inputs and expected outputs, for the given time zone
+ * and time.  */
+const struct tm  tm1 = {
+       /* Wed Jul  2 23:01:13 EDT 2008 (time_t=1215054073) */
+       .tm_sec         = 13,
+       .tm_min         = 1,
+       .tm_hour        = 23,
+       .tm_mday        = 2,
+       .tm_mon         = 6,
+       .tm_year        = 108,
+       .tm_wday        = 3,
+       .tm_yday        = 183,
+       .tm_isdst       = 1
+       };
+const struct test  Vec1[] = {
+       /* Testing fields one at a time, expecting to pass, using exact
+        * allowed length as what is needed.  */
+       /* Using tm0 for time: */
+       #define EXP(s)  sizeof(s)-1, s
+       { CQ("%a"), 3+1, EXP(CQ("Wed")) },
+       { CQ("%A"), 9+1, EXP(CQ("Wednesday")) },
+       { CQ("%b"), 3+1, EXP(CQ("Jul")) },
+       { CQ("%B"), 4+1, EXP(CQ("July")) },
+       { CQ("%c"), 24+1, EXP(CQ("Wed Jul  2 23:01:13 2008")) },
+       { CQ("%C"), 2+1, EXP(CQ("20")) },
+       { CQ("%d"), 2+1, EXP(CQ("02")) },
+       { CQ("%D"), 8+1, EXP(CQ("07/02/08")) },
+       { CQ("%e"), 2+1, EXP(CQ(" 2")) },
+       { CQ("%F"), 10+1, EXP(CQ("2008-07-02")) },
+       { CQ("%g"), 2+1, EXP(CQ("08")) },
+       { CQ("%G"), 4+1, EXP(CQ("2008")) },
+       { CQ("%h"), 3+1, EXP(CQ("Jul")) },
+       { CQ("%H"), 2+1, EXP(CQ("23")) },
+       { CQ("%I"), 2+1, EXP(CQ("11")) },
+       { CQ("%j"), 3+1, EXP(CQ("184")) },
+       { CQ("%k"), 2+1, EXP(CQ("23")) },
+       { CQ("%l"), 2+1, EXP(CQ("11")) },
+       { CQ("%m"), 2+1, EXP(CQ("07")) },
+       { CQ("%M"), 2+1, EXP(CQ("01")) },
+       { CQ("%n"), 1+1, EXP(CQ("\n")) },
+       { CQ("%p"), 2+1, EXP(CQ("PM")) },
+       { CQ("%r"), 11+1, EXP(CQ("11:01:13 PM")) },
+       { CQ("%R"), 5+1, EXP(CQ("23:01")) },
+       { CQ("%S"), 2+1, EXP(CQ("13")) },
+       { CQ("%t"), 1+1, EXP(CQ("\t")) },
+       { CQ("%T"), 8+1, EXP(CQ("23:01:13")) },
+       { CQ("%u"), 1+1, EXP(CQ("3")) },
+       { CQ("%U"), 2+1, EXP(CQ("26")) },
+       { CQ("%V"), 2+1, EXP(CQ("27")) },
+       { CQ("%w"), 1+1, EXP(CQ("3")) },
+       { CQ("%W"), 2+1, EXP(CQ("26")) },
+       { CQ("%x"), 8+1, EXP(CQ("07/02/08")) },
+       { CQ("%X"), 8+1, EXP(CQ("23:01:13")) },
+       { CQ("%y"), 2+1, EXP(CQ("08")) },
+       { CQ("%Y"), 4+1, EXP(CQ("2008")) },
+       { CQ("%z"), 5+1, EXP(CQ("-0400")) },
+       { CQ("%Z"), 3+1, EXP(CQ("EDT")) },
+       { CQ("%%"), 1+1, EXP(CQ("%")) },
+       #undef EXP
+       #define VEC(s)  s, sizeof(s), sizeof(s)-1, s
+       #define EXP(s)  sizeof(s), sizeof(s)-1, s
+       { VEC(CQ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")) },
+       { CQ("0123456789%%%h:`~"), EXP(CQ("0123456789%Jul:`~")) },
+       #undef VEC
+       #undef EXP
+       };
+#if YEAR_BASE == 1900  /* ( */
+/* Checks for very large years.  YEAR_BASE value relied upon so that the
+ * answer strings can be predetermined.
+ * Years more than 4 digits are not mentioned in the standard for %C, so the
+ * test for those cases are based on the design intent (which is to print the
+ * whole number, being the century).  */
+const struct tm  tmyr0 = {
+       /* Wed Jul  2 23:01:13 EDT [HUGE#] */
+       .tm_sec         = 13,
+       .tm_min         = 1,
+       .tm_hour        = 23,
+       .tm_mday        = 2,
+       .tm_mon         = 6,
+       .tm_year        = INT_MAX - YEAR_BASE/2,
+       .tm_wday        = 3,
+       .tm_yday        = 183,
+       .tm_isdst       = 1
+       };
+#if INT_MAX == 32767
+#  define YEAR CQ("33717")             /* INT_MAX + YEAR_BASE/2 */
+#  define CENT CQ("337")
+#  define Year    CQ("17")
+# elif INT_MAX == 2147483647
+#  define YEAR CQ("2147484597")
+#  define CENT CQ("21474845")
+#  define Year         CQ("97")
+# elif INT_MAX == 9223372036854775807
+#  define YEAR CQ("9223372036854776757")
+#  define CENT CQ("92233720368547777")
+#  define Year                  CQ("57")
+# else
+#  error "Unrecognized INT_MAX value:  enhance me to recognize what you have"
+#endif
+const struct test  Vecyr0[] = {
+       /* Testing fields one at a time, expecting to pass, using a larger
+        * allowed length than what is needed.  */
+       /* Using tmyr0 for time: */
+       #define EXP(s)  sizeof(s)-1, s
+       { CQ("%C"), OUTSIZE, EXP(CENT) },
+       { CQ("%c"), OUTSIZE, EXP(CQ("Wed Jul  2 23:01:13 ")YEAR) },
+       { CQ("%D"), OUTSIZE, EXP(CQ("07/02/")Year) },
+       { CQ("%F"), OUTSIZE, EXP(YEAR CQ("-07-02")) },
+       { CQ("%x"), OUTSIZE, EXP(CQ("07/02/")Year) },
+       { CQ("%y"), OUTSIZE, EXP(Year) },
+       { CQ("%Y"), OUTSIZE, EXP(YEAR) },
+       #undef EXP
+       };
+#undef YEAR
+#undef CENT
+#undef Year
+/* Checks for very large negative years.  YEAR_BASE value relied upon so that
+ * the answer strings can be predetermined.  */
+const struct tm  tmyr1 = {
+       /* Wed Jul  2 23:01:13 EDT [HUGE#] */
+       .tm_sec         = 13,
+       .tm_min         = 1,
+       .tm_hour        = 23,
+       .tm_mday        = 2,
+       .tm_mon         = 6,
+       .tm_year        = INT_MIN,
+       .tm_wday        = 3,
+       .tm_yday        = 183,
+       .tm_isdst       = 1
+       };
+#if INT_MAX == 32767
+#  define YEAR CQ("-30868")            /* INT_MIN + YEAR_BASE */
+#  define CENT CQ("-308")
+#  define Year     CQ("68")
+# elif INT_MAX == 2147483647
+#  define YEAR CQ("-2147481748")
+#  define CENT CQ("-21474817")
+#  define Year          CQ("48")
+# elif INT_MAX == 9223372036854775807
+#  define YEAR CQ("-9223372036854773908")
+#  define CENT CQ("-92233720368547739")
+#  define Year                   CQ("08")
+# else
+#  error "Unrecognized INT_MAX value:  enhance me to recognize what you have"
+#endif
+const struct test  Vecyr1[] = {
+       /* Testing fields one at a time, expecting to pass, using a larger
+        * allowed length than what is needed.  */
+       /* Using tmyr1 for time: */
+       #define EXP(s)  sizeof(s)-1, s
+       { CQ("%C"), OUTSIZE, EXP(CENT) },
+       { CQ("%c"), OUTSIZE, EXP(CQ("Wed Jul  2 23:01:13 ")YEAR) },
+       { CQ("%D"), OUTSIZE, EXP(CQ("07/02/")Year) },
+       { CQ("%F"), OUTSIZE, EXP(YEAR CQ("-07-02")) },
+       { CQ("%x"), OUTSIZE, EXP(CQ("07/02/")Year) },
+       { CQ("%y"), OUTSIZE, EXP(Year) },
+       { CQ("%Y"), OUTSIZE, EXP(YEAR) },
+       #undef EXP
+       };
+#undef YEAR
+#undef CENT
+#undef Year
+#endif /* YEAR_BASE ) */
+/* Checks for years just over zero (also test for s=60).
+ * Years less than 4 digits are not mentioned for %Y in the standard, so the
+ * test for that case is based on the design intent.  */
+const struct tm  tmyrzp = {
+       /* Wed Jul  2 23:01:60 EDT 0007 */
+       .tm_sec         = 60,
+       .tm_min         = 1,
+       .tm_hour        = 23,
+       .tm_mday        = 2,
+       .tm_mon         = 6,
+       .tm_year        = 7-YEAR_BASE,
+       .tm_wday        = 3,
+       .tm_yday        = 183,
+       .tm_isdst       = 1
+       };
+#define YEAR   CQ("0007")      /* Design intent:  %Y=%C%y */
+#define CENT   CQ("00")
+#define Year     CQ("07")
+const struct test  Vecyrzp[] = {
+       /* Testing fields one at a time, expecting to pass, using a larger
+        * allowed length than what is needed.  */
+       /* Using tmyrzp for time: */
+       #define EXP(s)  sizeof(s)-1, s
+       { CQ("%C"), OUTSIZE, EXP(CENT) },
+       { CQ("%c"), OUTSIZE, EXP(CQ("Wed Jul  2 23:01:60 ")YEAR) },
+       { CQ("%D"), OUTSIZE, EXP(CQ("07/02/")Year) },
+       { CQ("%F"), OUTSIZE, EXP(YEAR CQ("-07-02")) },
+       { CQ("%x"), OUTSIZE, EXP(CQ("07/02/")Year) },
+       { CQ("%y"), OUTSIZE, EXP(Year) },
+       { CQ("%Y"), OUTSIZE, EXP(YEAR) },
+       #undef EXP
+       };
+#undef YEAR
+#undef CENT
+#undef Year
+/* Checks for years just under zero.
+ * Negative years are not handled by the standard, so the vectors here are
+ * verifying the chosen implemtation.  */
+const struct tm  tmyrzn = {
+       /* Wed Jul  2 23:01:00 EDT -004 */
+       .tm_sec         = 00,
+       .tm_min         = 1,
+       .tm_hour        = 23,
+       .tm_mday        = 2,
+       .tm_mon         = 6,
+       .tm_year        = -4-YEAR_BASE,
+       .tm_wday        = 3,
+       .tm_yday        = 183,
+       .tm_isdst       = 1
+       };
+#define YEAR   CQ("-004")
+#define CENT   CQ("-0")
+#define Year     CQ("04")
+const struct test  Vecyrzn[] = {
+       /* Testing fields one at a time, expecting to pass, using a larger
+        * allowed length than what is needed.  */
+       /* Using tmyrzn for time: */
+       #define EXP(s)  sizeof(s)-1, s
+       { CQ("%C"), OUTSIZE, EXP(CENT) },
+       { CQ("%c"), OUTSIZE, EXP(CQ("Wed Jul  2 23:01:00 ")YEAR) },
+       { CQ("%D"), OUTSIZE, EXP(CQ("07/02/")Year) },
+       { CQ("%F"), OUTSIZE, EXP(YEAR CQ("-07-02")) },
+       { CQ("%x"), OUTSIZE, EXP(CQ("07/02/")Year) },
+       { CQ("%y"), OUTSIZE, EXP(Year) },
+       { CQ("%Y"), OUTSIZE, EXP(YEAR) },
+       #undef EXP
+       };
+#undef YEAR
+#undef CENT
+#undef Year
+const struct list  ListYr[] = {
+       { &tmyrzp, Vecyrzp, sizeof(Vecyrzp)/sizeof(Vecyrzp[0]) },
+       { &tmyrzn, Vecyrzn, sizeof(Vecyrzn)/sizeof(Vecyrzn[0]) },
+       #if YEAR_BASE == 1900
+       { &tmyr0, Vecyr0, sizeof(Vecyr0)/sizeof(Vecyr0[0]) },
+       { &tmyr1, Vecyr1, sizeof(Vecyr1)/sizeof(Vecyr1[0]) },
+       #endif
+       };
+/* List of tests to be run */
+const struct list  List[] = {
+       { &tm0, Vec0, sizeof(Vec0)/sizeof(Vec0[0]) },
+       { &tm1, Vec1, sizeof(Vec1)/sizeof(Vec1[0]) },
+       };
+#if defined(STUB_getenv_r)
+char *
+_getenv_r(struct _reent *p, const char *cp) { return getenv(cp); }
+#endif
+int
+main(void)
+{
+int  i, l, errr=0, erro=0, tot=0;
+const char  *cp;
+CHAR  out[OUTSIZE];
+size_t  ret;
+/* Set timezone so that %z and %Z tests come out right */
+cp = TZ;
+if((i=putenv(cp)))  {
+    printf( "putenv(%s) FAILED, ret %d\n", cp, i);
+    return(-1);
+    }
+if(strcmp(getenv("TZ"),strchr(TZ,'=')+1))  {
+    printf( "TZ not set properly in environment\n");
+    return(-2);
+    }
+tzset();
+#if defined(VERBOSE)
+printf("_timezone=%d, _daylight=%d, _tzname[0]=%s, _tzname[1]=%s\n", _timezone, _daylight, _tzname[0], _tzname[1]);
+{
+long offset;
+__tzinfo_type *tz = __gettzinfo ();
+/* The sign of this is exactly opposite the envvar TZ.  We
+   could directly use the global _timezone for tm_isdst==0,
+   but have to use __tzrule for daylight savings.  */
+printf("tz->__tzrule[0].offset=%d, tz->__tzrule[1].offset=%d\n", tz->__tzrule[0].offset, tz->__tzrule[1].offset);
+}
+#endif
+/* Run all of the exact-length tests as-given--results should match */
+for(l=0; l<sizeof(List)/sizeof(List[0]); l++)  {
+    const struct list  *test = &List[l];
+    for(i=0; i<test->cnt; i++)  {
+       tot++;  /* Keep track of number of tests */
+       ret = strftime(out, test->vec[i].max, test->vec[i].fmt, test->tms);
+       if(ret != test->vec[i].ret)  {
+           errr++;
+           fprintf(stderr,
+               "ERROR:  return %d != %d expected for List[%d].vec[%d]\n",
+                                               ret, test->vec[i].ret, l, i);
+           }
+       if(strncmp(out, test->vec[i].out, test->vec[i].max-1))  {
+           erro++;
+           fprintf(stderr,
+               "ERROR:  \"%"SFLG"s\" != \"%"SFLG"s\" expected for List[%d].vec[%d]\n",
+                                               out, test->vec[i].out, l, i);
+           }
+       }
+    }
+/* Run all of the exact-length tests with the length made too short--expect to
+ * fail.  */
+for(l=0; l<sizeof(List)/sizeof(List[0]); l++)  {
+    const struct list  *test = &List[l];
+    for(i=0; i<test->cnt; i++)  {
+       tot++;  /* Keep track of number of tests */
+       ret = strftime(out, test->vec[i].max-1, test->vec[i].fmt, test->tms);
+       if(ret != 0)  {
+           errr++;
+           fprintf(stderr,
+               "ERROR:  return %d != %d expected for List[%d].vec[%d]\n",
+                                               ret, 0, l, i);
+           }
+       /* Almost every conversion puts out as many characters as possible, so
+        * go ahead and test the output even though have failed.  (The test
+        * times chosen happen to not hit any of the cases that fail this, so it
+        * works.)  */
+       if(strncmp(out, test->vec[i].out, test->vec[i].max-1-1))  {
+           erro++;
+           fprintf(stderr,
+               "ERROR:  \"%"SFLG"s\" != \"%"SFLG"s\" expected for List[%d].vec[%d]\n",
+                                               out, test->vec[i].out, l, i);
+           }
+       }
+    }
+/* Run all of the special year test cases */
+for(l=0; l<sizeof(ListYr)/sizeof(ListYr[0]); l++)  {
+    const struct list  *test = &ListYr[l];
+    for(i=0; i<test->cnt; i++)  {
+       tot++;  /* Keep track of number of tests */
+       ret = strftime(out, test->vec[i].max, test->vec[i].fmt, test->tms);
+       if(ret != test->vec[i].ret)  {
+           errr++;
+           fprintf(stderr,
+               "ERROR:  return %d != %d expected for ListYr[%d].vec[%d]\n",
+                                               ret, test->vec[i].ret, l, i);
+           }
+       if(strncmp(out, test->vec[i].out, test->vec[i].max-1))  {
+           erro++;
+           fprintf(stderr,
+               "ERROR:  \"%"SFLG"s\" != \"%"SFLG"s\" expected for ListYr[%d].vec[%d]\n",
+                                               out, test->vec[i].out, l, i);
+           }
+       }
+    }
+#define STRIZE(f)      #f
+#define NAME(f)        STRIZE(f)
+printf(NAME(strftime) "() test ");
+if(errr || erro)  printf("FAILED %d/%d of", errr, erro);
+  else    printf("passed");
+printf(" %d test cases.\n", tot);
+return(errr || erro);
+}
+#endif /* defined(_REGRESSION_TEST) ] */
index 4859404a37da27f26d6df9ee056fa092d4d350d6..cecd0f5f5ee7aea81c69eddbacef1bc6c882a10d 100644 (file)
@@ -44,6 +44,8 @@ Number of days elapsed since last January 1, between 0 and 365 inclusive.
 @item tm_isdst
 Daylight Savings Time flag: positive means DST in effect, zero means DST
 not in effect, negative means no information about DST is available.
+Although for mktime(), negative means that it should decide if DST is in
+effect or not.
 @end table
 
 @menu
@@ -54,7 +56,7 @@ not in effect, negative means no information about DST is available.
 * gmtime::      Convert time to UTC (GMT) traditional representation
 * localtime::   Convert time to local representation
 * mktime::      Convert time to arithmetic representation
-* strftime::    Flexible calendar time formatter
+* strftime::    Convert date and time to a user-formatted string
 * time::        Get current calendar time (as single number)
 * __tz_lock::   Lock time zone global variables
 * tzset::       Set timezone info
diff --git a/newlib/libc/time/wcsftime.c b/newlib/libc/time/wcsftime.c
new file mode 100644 (file)
index 0000000..323a6c2
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * wcsftime.c
+ * Original Author:    Craig Howland, for Newlib
+ *
+ * Source actually uses strftime.c.
+ * Documentation for wcsftime() here, with minimal overlap.
+ */
+
+/*
+FUNCTION
+<<wcsftime>>--convert date and time to a formatted wide-character string
+
+INDEX
+       wcsftime
+
+ANSI_SYNOPSIS
+       #include <time.h>
+       #include <wchar.h>
+       size_t wcsftime(wchar_t *<[s]>, size_t <[maxsize]>,
+                       const wchar_t *<[format]>, const struct tm *<[timp]>);
+
+DESCRIPTION
+<<strftime>> is equivalent to <<strftime>>, except that:
+The argument s points to the initial element of an array of wide characters
+into which the generated output is to be placed.
+The argument maxsize indicates the limiting number of wide characters.
+The argument format is a wide-character string and the conversion specifiers
+are replaced by corresponding sequences of wide characters.
+The return value indicates the number of wide characters.
+See <<strftime>> for the details of the format specifiers.
+
+RETURNS
+When the formatted time takes up no more than <[maxsize]> wide characters,
+the result is the length of the formatted wide string.  Otherwise, if the
+formatting operation was abandoned due to lack of room, the result is
+<<0>>, and the wide-character string starting at <[s]> corresponds to just those
+parts of <<*<[format]>>> that could be completely filled in within the
+<[maxsize]> limit.
+
+PORTABILITY
+C99 and POSIX require <<wcsftime>>, but do not specify the contents of
+<<*<[s]>>> when the formatted string would require more than
+<[maxsize]> characters.  Unrecognized specifiers and fields of
+<<timp>> that are out of range cause undefined results.  Since some
+formats expand to 0 bytes, it is wise to set <<*<[s]>>> to a nonzero
+value beforehand to distinguish between failure and an empty string.
+This implementation does not support <<s>> being NULL, nor overlapping
+<<s>> and <<format>>.
+
+SEEALSO
+<<strftime>>
+
+<<wcsftime>> requires no supporting OS subroutines.
+*/
+
+#include <time.h>
+#include <wchar.h>
+#define MAKE_WCSFTIME
+#include "../time/strftime.c"
This page took 0.080624 seconds and 5 git commands to generate.