stdcxx-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From se...@apache.org
Subject svn commit: r500920 - in /incubator/stdcxx/trunk/tests: self/0.printf.cpp src/printf.cpp
Date Mon, 29 Jan 2007 01:58:11 GMT
Author: sebor
Date: Sun Jan 28 17:58:10 2007
New Revision: 500920

URL: http://svn.apache.org/viewvc?view=rev&rev=500920
Log:
2007-01-28  Martin Sebor  <sebor@roguewave.com>

	* printf.cpp (_rw_fmtspec): Avoided handling '$' when it's immediately
	followed by a closing curly brace ('}'). Ditto for '@'.
	(_rw_pvasnprintf): New. Implements the guts of rw_vasnprintf without
	NUL-terminating the buffer to make it easier to call it recursively.
	(_rw_vasnprintf_ext): Recognized the new %{@} directive (nested format
	specifier). Called _rw_fmtexpr to handle it.
	(_rw_fmtexpr): Handled the %{@} directive (nested format specifier).
	* test/printf.cpp (test_envvar): Added test cases exercising
	unconditional assignment (the %{$<param>!:<word>} directive).
	(test_nested_format): Exercised the %{@} directive.

Modified:
    incubator/stdcxx/trunk/tests/self/0.printf.cpp
    incubator/stdcxx/trunk/tests/src/printf.cpp

Modified: incubator/stdcxx/trunk/tests/self/0.printf.cpp
URL: http://svn.apache.org/viewvc/incubator/stdcxx/trunk/tests/self/0.printf.cpp?view=diff&rev=500920&r1=500919&r2=500920
==============================================================================
--- incubator/stdcxx/trunk/tests/self/0.printf.cpp (original)
+++ incubator/stdcxx/trunk/tests/self/0.printf.cpp Sun Jan 28 17:58:10 2007
@@ -2146,6 +2146,21 @@
     TEST ("[%{$*+*}]",  "NULL",     "WORD", 0, "[WORD]");
     TEST ("[%{$*+*}]",  "UNSET",    "WORD", 0, "[]");
 
+    //////////////////////////////////////////////////////////////////
+
+    // exercise unconditional assignment
+    TEST ("[%{$*!:*}]", "NOT_NULL", "WORD1", 0, "[WORD1]");
+    TEST ("[%{$*!:*}]", "NULL",     "WORD2", 0, "[WORD2]");
+    TEST ("[%{$*!:*}]", "UNSET",    "WORD3", 0, "[WORD3]");
+
+    TEST ("[%{$*!:*}]", "NOT_NULL", "WORD4", 0, "[WORD4]");
+    TEST ("[%{$*!:*}]", "NULL",     "WORD5", 0, "[WORD5]");
+    TEST ("[%{$*!:*}]", "UNSET",    "WORD6", 0, "[WORD6]");
+
+    //////////////////////////////////////////////////////////////////
+
+    // exercise assignment of a formatted string
+    TEST ("[%{$FOO!:@}, %{$FOO}]", "%s", "bar", 0, "[bar, bar]");
 }
 
 /***********************************************************************/
@@ -2781,6 +2796,42 @@
 /***********************************************************************/
 
 static void
+test_nested_format ()
+{
+    //////////////////////////////////////////////////////////////////
+    printf ("%s\n", "extension: \"%{@}\" nested format directive");
+
+    TEST ("%{@}",        "",     0,    0, "");
+    TEST ("%{@}",        "a",    0,    0, "a");
+    TEST ("%{@}",        "ab",   0,    0, "ab");
+    TEST ("%{@}",        "abc",  0,    0, "abc");
+    TEST ("%{@}",        "%d",   0,    0, "0");
+    TEST ("%{@}",        "%d",   1,    0, "1");
+    TEST ("%{@}",        "%d",  12,    0, "12");
+    TEST ("%{@}",        "%s",  "x",   0, "x");
+    TEST ("%{@}",        "%s",  "xy",  0, "xy");
+    TEST ("%{@}",        "%s",  "xyz", 0, "xyz");
+    TEST ("x%{@}",       "%s",  "yz",  0, "xyz");
+    TEST ("%{@}z",       "%s",  "xy",  0, "xyz");
+    TEST ("x%{@}z",      "%s",  "y",   0, "xyz");
+
+    TEST ("ABC%{@}F",    "D%{@}E", "",    0, "ABCDEF");
+    TEST ("ABC%{@}GH",   "D%{@}F", "e",   0, "ABCDeFGH");
+    TEST ("ABC%{@}HIJ",  "D%{@}G", "ef",  0, "ABCDefGHIJ");
+    TEST ("ABC%{@}IJKL", "D%{@}H", "efg", 0, "ABCDefgHIJKL");
+
+    TEST ("AB%{@}MN", "CD%{@}KL", "EF%{@}IJ", "gh", "ABCDEFghIJKLMN");
+
+    TEST ("A%{@}C%{@}E%{@}G", "B",  "D",  "F", "ABCDEFG");
+    TEST ("A%{@}C%{@}E",      "%s", "B",  "D", "ABCDE");
+    TEST ("A%{@}C%{@}E",      "B",  "%s", "D", "ABCDE");
+
+    TEST ("ABC%sGHI%{@}XYZ", "DEF", "%s%1$s", "JKL", "ABCDEFGHIJKLJKLXYZ");
+}
+
+/***********************************************************************/
+
+static void
 test_malformed_directives ()
 {
     //////////////////////////////////////////////////////////////////
@@ -2880,6 +2931,8 @@
     test_user_defined_formatting ();
 
     test_bufsize ();
+
+    test_nested_format ();
 
     test_malformed_directives ();
 

Modified: incubator/stdcxx/trunk/tests/src/printf.cpp
URL: http://svn.apache.org/viewvc/incubator/stdcxx/trunk/tests/src/printf.cpp?view=diff&rev=500920&r1=500919&r2=500920
==============================================================================
--- incubator/stdcxx/trunk/tests/src/printf.cpp (original)
+++ incubator/stdcxx/trunk/tests/src/printf.cpp Sun Jan 28 17:58:10 2007
@@ -198,7 +198,7 @@
     const char* const fmtbeg = fmt;
 
     if (ext) {
-        if ('$' == *fmt) {
+        if ('$' == *fmt && '}' != fmt [1]) {
 
             // %{$<string>}: introduces the name of an environment
             // variable (or parameter)
@@ -264,7 +264,8 @@
         }
     }
 
-    if (ext && '@' == *fmt) {
+    if (ext && '@' == *fmt && '}' != fmt [1]) {
+        // @<decimal-number>
 
         ++fmt;
 
@@ -662,34 +663,13 @@
 
 /********************************************************************/
 
-_TEST_EXPORT int
-rw_vasnprintf (char **pbuf, size_t *pbufsize, const char *fmt, va_list varg)
+// implements rw_vasnprintf, but may be called recursively
+static int
+_rw_pvasnprintf (Buffer &buf, const char *fmt, va_list *pva)
 {
-    va_list *pva;
-
-    va_list vacpy;
-    _RWSTD_VA_COPY (vacpy, varg);
-
-    pva = &vacpy;
-
-// do not use varg of vacpy below this point -- use *pva instead
-#define varg  DONT_TOUCH_ME
-#define vacpy DONT_TOUCH_ME
-
-    size_t default_bufsize = 1024;
-    if (0 == pbufsize)
-        pbufsize = &default_bufsize;
-
-    Buffer buf = { pbuf, pbufsize, _RWSTD_SIZE_MAX, 0 };
-
-    RW_ASSERT (0 != buf.pbuf);
-    RW_ASSERT (0 != buf.pbufsize);
-
-    // save the initial value of `pbuf'
-    char* const pbuf_save = *buf.pbuf;
-
-    // when appending to the buffer
-    size_t append_offset = 0;
+    // save the length of the initial subsequence already
+    // in the buffer
+    const size_t begoff = buf.endoff;
 
     // local buffer for a small number of conversion specifiers
     // will grow dynamically if their number exceeds its capacity
@@ -702,54 +682,16 @@
 
     char fmtspec [64];
 
-    char *next = *buf.pbuf;
-
     size_t spec_bufsize = sizeof specbuf / sizeof *specbuf;
     size_t paramno = 0;
 
-    if ('%' == fmt [0] && '{' == fmt [1] && '+' == fmt [2] && '}'
== fmt [3]) {
-        // when the format string begins with the special %{+}
-        // directive append to the buffer instead of writing
-        // over it
-        fmt += 4;
-        append_offset = *buf.pbuf ? strlen (*buf.pbuf) : 0;
-        buf.endoff    = append_offset;
-    }
-    else if (*buf.pbuf)
-        **buf.pbuf = '\0';
-
-    if ('%' == fmt [0] && '{' == fmt [1]) {
-        if ('*' == fmt [2] && '}' == fmt [3]) {
-            const int n = va_arg (*pva, int);
-            if (n < 0) {
-#ifdef EINVAL
-                errno = EINVAL;
-#endif   // EINVAL
-                goto fail;
-            }
-
-            buf.maxsize = size_t (n);
-            fmt += 4;
-        }
-        else if (isdigit (fmt [2])) {
-
-            char* end = 0;
-            const ULong n = strtoul (fmt + 2, &end, 0);
-            if ('}' == *end) {
-                buf.maxsize = n;
-                fmt         = end + 1;
-            }
-        }
-    }
-
     for (const char *fc = fmt; *fc; ) {
 
         const char* const pcnt = strchr (fc, '%');
 
         size_t nchars = pcnt ? pcnt - fmt : strlen (fc);
 
-        next = _rw_bufcat (buf, fmt, nchars);
-        if (0 == next)
+        if (0 == _rw_bufcat (buf, fmt, nchars))
             goto fail;
 
         RW_ASSERT (0 != *buf.pbuf);
@@ -762,8 +704,7 @@
 
         if ('%' == *fc) {
             // handle "%%"
-            next = _rw_bufcat (buf, "%", 1);
-            if (0 == next)
+            if (0 == _rw_bufcat (buf, "%", 1))
                 goto fail;
 
             fmt = ++fc;
@@ -792,8 +733,7 @@
 
             if (0 == endbrace || fc == endbrace) {
                 const size_t flen = strlen (fc -= 2);
-                next = _rw_bufcat (buf, fc, flen);
-                if (0 == next)
+                if (0 == _rw_bufcat (buf, fc, flen))
                     goto fail;
 
                 fc += flen;
@@ -809,7 +749,7 @@
             fmtspec [fmtlen] = '\0';
 
             // compute the length of the buffer so far
-            const size_t buflen = next - *buf.pbuf;
+            const size_t buflen = _rw_bufcat (buf, "", 0) - *buf.pbuf;
 
             RW_ASSERT (paramno < spec_bufsize);
 
@@ -921,12 +861,6 @@
 
             RW_ASSERT (len + buflen <= *buf.pbufsize);
 
-            // adjust the next pointer to point to the terminating
-            // NUL in the (possibly reallocated) buffer
-            next = *buf.pbuf + buflen + len;
-
-            RW_ASSERT (next == *buf.pbuf + buf.endoff);
-
             fc += speclen + 1;
             if (fc < endbrace)
                 fc = endbrace + 1;
@@ -947,13 +881,10 @@
                 if (-1 == pspec [paramno].paramno)
                     ++paramno;
 
-                next += len;
-                fc   += speclen;
+                fc += speclen;
             }
-            else {
-                next = _rw_bufcat (buf, "%", 1);
-                if (0 == next)
-                    goto fail;
+            else if (0 == _rw_bufcat (buf, "%", 1)) {
+                goto fail;
             }
         }
 
@@ -967,40 +898,112 @@
     if (pspec != specbuf)
         free (pspec);
 
-    // NUL-terminate
-    next = _rw_bufcat (buf, "", 1);
-    if (0 == next)
-        goto fail;
+    RW_ASSERT (begoff <= buf.endoff);
 
     // return the number of characters appended to the buffer
-    // not including the terminating NUL
-    return int ((next - *buf.pbuf) - append_offset - 1);
+    // buffer isn't necessarily NUL terminated at this point
+    return int (buf.endoff - begoff);
 
 fail: // function failed
 
-    const int error = errno;
-
-    fprintf (stderr, "%s:%d: rw_vasnprintf(%p, %p, \"%s\", va_list) "
-             "error: errno = %d: %s\n",
-             __FILE__, __LINE__, (void*)buf.pbuf, (void*)buf.pbufsize, fmt,
-             error, strerror (error));
-
     for (size_t i = 0; i != paramno; ++i)
         free (pspec [i].strarg);
 
     if (pspec != specbuf)
         free (pspec);
 
-    if (*buf.pbuf != pbuf_save) {
-        // free any allocated memory
-        free (*buf.pbuf);
-        *buf.pbuf = 0;
+    return -1;
+}
+
+
+_TEST_EXPORT int
+rw_vasnprintf (char **pbuf, size_t *pbufsize, const char *fmt, va_list varg)
+{
+    va_list *pva;
+
+    va_list vacpy;
+    _RWSTD_VA_COPY (vacpy, varg);
+
+    pva = &vacpy;
+
+// do not use varg or vacpy below this point -- use *pva instead
+#define varg  DONT_TOUCH_ME
+#define vacpy DONT_TOUCH_ME
+
+    size_t default_bufsize = 1024;
+    if (0 == pbufsize)
+        pbufsize = &default_bufsize;
+
+    Buffer buf = { pbuf, pbufsize, _RWSTD_SIZE_MAX, 0 };
+
+    // save the initial value of `pbuf'
+    char* const pbuf_save = *buf.pbuf;
+
+    RW_ASSERT (0 != buf.pbuf);
+    RW_ASSERT (0 != buf.pbufsize);
+
+    if ('%' == fmt [0] && '{' == fmt [1] && '+' == fmt [2] && '}'
== fmt [3]) {
+        // when the format string begins with the special %{+}
+        // directive append to the buffer instead of writing
+        // over it
+        fmt        += 4;
+        buf.endoff  = *buf.pbuf ? strlen (*buf.pbuf) : 0;
     }
+    else if (*buf.pbuf)
+        **buf.pbuf = '\0';
 
-    if (errno != error)
-        errno = error;
+    if ('%' == fmt [0] && '{' == fmt [1]) {
+        if ('*' == fmt [2] && '}' == fmt [3]) {
+            const int n = va_arg (*pva, int);
+            if (n < 0) {
+#ifdef EINVAL
+                errno = EINVAL;
+#endif   // EINVAL
+                return -1;
+            }
 
-    return -1;
+            buf.maxsize = size_t (n);
+            fmt += 4;
+        }
+        else if (isdigit (fmt [2])) {
+
+            char* end = 0;
+            const ULong n = strtoul (fmt + 2, &end, 0);
+            if ('}' == *end) {
+                buf.maxsize = n;
+                fmt         = end + 1;
+            }
+        }
+    }
+
+    // format buffer w/o appending terminating NUL
+    const int len = _rw_pvasnprintf (buf, fmt, pva);
+
+    // append terminating NUL
+    if (len < 0 || !_rw_bufcat (buf, "", 1)) {
+
+        const int error = errno;
+
+        fprintf (stderr, "%s:%d: rw_vasnprintf(%p, %p, \"%s\", va_list) "
+                 "error: errno = %d: %s\n",
+                 __FILE__, __LINE__, (void*)buf.pbuf, (void*)buf.pbufsize,
+                 fmt, error, strerror (error));
+
+        if (*buf.pbuf != pbuf_save) {
+            // free any allocated memory
+            free (*buf.pbuf);
+            *buf.pbuf = 0;
+        }
+
+        if (errno != error) {
+            // reset errno if it's been modified since it was saved
+            errno = error;
+        }
+
+        return len < 0 ? len : -1;
+    }
+
+    return len;
 
 #undef varg
 #undef vacpy
@@ -2555,6 +2558,14 @@
         len = 0;
         break;
 
+    case '@': {   // %{@}
+        // user-defined formatting string
+        spec.param.ptr_ = PARAM (ptr_);
+        const char* const tmp_fmt = (const char*)spec.param.ptr_;
+        len = _rw_pvasnprintf (buf, tmp_fmt, pva);
+        break;
+    }
+
     case '?':   // %{?}
         // beginning of an if clause
         spec.param.int_ = PARAM (int_);
@@ -2955,10 +2966,32 @@
         param = va_arg (*pva, char*);
     }
 
+    char* fmtword = 0;
+
     if ('*' == *word) {
         // extract "word" from the argument list
         word = va_arg (*pva, char*);
     }
+    else if ('@' == *word) {
+        // extract formatting directive from the argument list
+        // and set word to the result of processing it
+        const char* const fmt = va_arg (*pva, char*);
+
+        size_t dummy_size = 0;   // unused
+        Buffer tmpbuf = { &fmtword, &dummy_size, _RWSTD_SIZE_MAX, 0 };
+        const int len = _rw_pvasnprintf (tmpbuf, fmt, pva);
+        if (len < 0)
+            return -1;
+
+        // add terminating NUL
+        if (0 == _rw_bufcat (tmpbuf, "", 1)) {
+            free (fmtword);
+            return -1;
+        }
+
+        // set word to the formatted string
+        word = *tmpbuf.pbuf;
+    }
 
     // retrieve the value of the parameter from the environments
     const char* val = getenv (param);
@@ -3050,6 +3083,11 @@
         break;
 
     default:
+        if (0 == val) {
+            // undefined variable
+            val = "";
+        }
+
         break;
     }
 
@@ -3081,16 +3119,24 @@
 
         char text [256];
         len = sprintf (text, "%%{$%.*s}", int (sizeof text - 3), spec.strarg);
-        if (0 == _rw_bufcat (buf, text, size_t (len)))
+        if (0 == _rw_bufcat (buf, text, size_t (len))) {
+            free (fmtword);
             return -1;
+        }
     }
     else {
         // format the value of the variable (after assignment
         // if it takes place)
-        if (0 == _rw_bufcat (buf, val, size_t (len)))
+        if (0 == _rw_bufcat (buf, val, size_t (len))) {
+            free (fmtword);
             return -1;
+        }
     }
 
+    // free the formatted word (if any)
+    free (fmtword);
+
+    // free the string allocated in _rw_fmtspec()
     free (spec.strarg);
     spec.strarg     = 0;
     spec.param.ptr_ = _RWSTD_CONST_CAST (char*, val);



Mime
View raw message