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 <[EMAIL PROTECTED]>
* 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 [EMAIL PROTECTED] directive
(nested format
specifier). Called _rw_fmtexpr to handle it.
(_rw_fmtexpr): Handled the [EMAIL PROTECTED] directive (nested format
specifier).
* test/printf.cpp (test_envvar): Added test cases exercising
unconditional assignment (the %{$<param>!:<word>} directive).
(test_nested_format): Exercised the [EMAIL PROTECTED] 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: \"[EMAIL PROTECTED]" nested format directive");
+
+ TEST ("[EMAIL PROTECTED]", "", 0, 0, "");
+ TEST ("[EMAIL PROTECTED]", "a", 0, 0, "a");
+ TEST ("[EMAIL PROTECTED]", "ab", 0, 0, "ab");
+ TEST ("[EMAIL PROTECTED]", "abc", 0, 0, "abc");
+ TEST ("[EMAIL PROTECTED]", "%d", 0, 0, "0");
+ TEST ("[EMAIL PROTECTED]", "%d", 1, 0, "1");
+ TEST ("[EMAIL PROTECTED]", "%d", 12, 0, "12");
+ TEST ("[EMAIL PROTECTED]", "%s", "x", 0, "x");
+ TEST ("[EMAIL PROTECTED]", "%s", "xy", 0, "xy");
+ TEST ("[EMAIL PROTECTED]", "%s", "xyz", 0, "xyz");
+ TEST ("[EMAIL PROTECTED]", "%s", "yz", 0, "xyz");
+ TEST ("[EMAIL PROTECTED]", "%s", "xy", 0, "xyz");
+ TEST ("[EMAIL PROTECTED]", "%s", "y", 0, "xyz");
+
+ TEST ("[EMAIL PROTECTED]", "[EMAIL PROTECTED]", "", 0, "ABCDEF");
+ TEST ("[EMAIL PROTECTED]", "[EMAIL PROTECTED]", "e", 0, "ABCDeFGH");
+ TEST ("[EMAIL PROTECTED]", "[EMAIL PROTECTED]", "ef", 0, "ABCDefGHIJ");
+ TEST ("[EMAIL PROTECTED]", "[EMAIL PROTECTED]", "efg", 0, "ABCDefgHIJKL");
+
+ TEST ("[EMAIL PROTECTED]", "[EMAIL PROTECTED]", "[EMAIL PROTECTED]", "gh",
"ABCDEFghIJKLMN");
+
+ TEST ("[EMAIL PROTECTED]@[EMAIL PROTECTED]", "B", "D", "F", "ABCDEFG");
+ TEST ("[EMAIL PROTECTED]@}E", "%s", "B", "D", "ABCDE");
+ TEST ("[EMAIL PROTECTED]@}E", "B", "%s", "D", "ABCDE");
+
+ TEST ("[EMAIL PROTECTED]", "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 '@': { // [EMAIL PROTECTED]
+ // 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);