aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWerner Koch <[email protected]>2018-11-26 19:22:24 +0000
committerWerner Koch <[email protected]>2018-11-26 19:22:24 +0000
commitbd8668c120ac0f725edb092b2c4ca49ffdb78ed2 (patch)
treeffeea782e9dc6435822f3b3f1c950c7222ff93b9
parentcore: Add a limited version of gpgrt_ftruncate. (diff)
downloadlibgpg-error-bd8668c120ac0f725edb092b2c4ca49ffdb78ed2.tar.gz
libgpg-error-bd8668c120ac0f725edb092b2c4ca49ffdb78ed2.zip
core: New functions gpgrt_fprintf_sf anf gpgrt_fprintf_sf_unlocked.
* src/gpg-error.h.in (gpgrt_string_filter_t): New type. (gpgrt_fprintf_sf, gpgrt_fprintf_sf_unlocked): New. * src/gpg-error.vers, src/gpg-error.def.in: Add them. * src/visibility.c (gpgrt_fprintf_sf): New. (gpgrt_fprintf_sf_unlocked): New. * src/estream-printf.c (pr_string): Add and use args sf, sfvalue and string_no. (do_format): Add args sf and sfvalue. Keep a string format counter. (_gpgrt_estream_format): Add args sf and sfvalue. Change all callers to provide NULL for them. * src/estream.c (_gpgrt_vfprintf_unlocked, _gpgrt_vfprintf): Add sf and sfvalue and adjust all callers. (do_print_stream): Ditto. * tests/t-printf.c (stream_to_string): New. (struct sfstate_s): New. (string_filter): New. (check_fprintf_sf): New. (main): Call new test. -- The actual reason to implement these functions is to enhance the internal logging function with a filter to sanitized strings so that control values or other things can be quoted. Signed-off-by: Werner Koch <[email protected]>
-rw-r--r--NEWS4
-rw-r--r--src/estream-printf.c67
-rw-r--r--src/estream-printf.h4
-rw-r--r--src/estream.c13
-rw-r--r--src/gpg-error.def.in3
-rw-r--r--src/gpg-error.h.in14
-rw-r--r--src/gpg-error.vers3
-rw-r--r--src/gpgrt-int.h6
-rw-r--r--src/logging.c2
-rw-r--r--src/visibility.c42
-rw-r--r--src/visibility.h4
-rw-r--r--tests/t-printf.c112
12 files changed, 239 insertions, 35 deletions
diff --git a/NEWS b/NEWS
index f1facf4..5804819 100644
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,10 @@ Noteworthy changes in version 1.33 (unreleased) [C24/A24/R_]
* Interface changes relative to the 1.28 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gpgrt_cmp_version New.
+ gpgrt_ftruncate New but limited functionality.
+ gpgrt_string_filter_t New.
+ gpgrt_fprintf_sf New.
+ gpgrt_fprintf_sf_unlocked New.
Noteworthy changes in version 1.32 (2018-07-12) [C24/A24/R3]
diff --git a/src/estream-printf.c b/src/estream-printf.c
index eb6fa3a..b9775a6 100644
--- a/src/estream-printf.c
+++ b/src/estream-printf.c
@@ -1196,7 +1196,8 @@ pr_char (estream_printf_out_t outfnc, void *outfncarg,
/* "s" formatting. */
static int
pr_string (estream_printf_out_t outfnc, void *outfncarg,
- argspec_t arg, value_t value, size_t *nbytes)
+ argspec_t arg, value_t value, size_t *nbytes,
+ gpgrt_string_filter_t sf, void *sfvalue, int string_no)
{
int rc;
size_t n;
@@ -1205,6 +1206,11 @@ pr_string (estream_printf_out_t outfnc, void *outfncarg,
if (arg->vt != VALTYPE_STRING)
return -1;
string = value.a_string;
+ if (sf)
+ string = sf (value.a_string, string_no, sfvalue);
+ else
+ string = value.a_string;
+
if (!string)
string = "(null)";
if (arg->precision >= 0)
@@ -1222,12 +1228,12 @@ pr_string (estream_printf_out_t outfnc, void *outfncarg,
{
rc = pad_out (outfnc, outfncarg, ' ', arg->width - n, nbytes);
if (rc)
- return rc;
+ goto leave;
}
rc = outfnc (outfncarg, string, n);
if (rc)
- return rc;
+ goto leave;
*nbytes += n;
if ((arg->flags & FLAG_LEFT_JUST)
@@ -1235,10 +1241,16 @@ pr_string (estream_printf_out_t outfnc, void *outfncarg,
{
rc = pad_out (outfnc, outfncarg, ' ', arg->width - n, nbytes);
if (rc)
- return rc;
+ goto leave;
}
- return 0;
+ rc = 0;
+
+ leave:
+ if (sf) /* Tell the filter to release resources. */
+ sf (value.a_string, -1, sfvalue);
+
+ return rc;
}
@@ -1337,14 +1349,18 @@ pr_bytes_so_far (estream_printf_out_t outfnc, void *outfncarg,
/* Run the actual formatting. OUTFNC and OUTFNCARG are the output
- functions. FORMAT is format string ARGSPECS is the parsed format
- string, ARGSPECS_LEN the number of items in ARGSPECS. VALUETABLE
- holds the values and may be directly addressed using the position
- arguments given by ARGSPECS. MYERRNO is used for the "%m"
- conversion. NBYTES well be updated to reflect the number of bytes
- send to the output function. */
+ * functions. FORMAT is format string ARGSPECS is the parsed format
+ * string, ARGSPECS_LEN the number of items in ARGSPECS.
+ * STRING_FILTER is an optional function to filter string (%s) args;
+ * it is called with the original string and the count of already
+ * processed %s arguments. Its return value will be used instead of
+ * the original string. VALUETABLE holds the values and may be
+ * directly addressed using the position arguments given by ARGSPECS.
+ * MYERRNO is used for the "%m" conversion. NBYTES well be updated to
+ * reflect the number of bytes send to the output function. */
static int
do_format (estream_printf_out_t outfnc, void *outfncarg,
+ gpgrt_string_filter_t sf, void *sfvalue,
const char *format, argspec_t argspecs, size_t argspecs_len,
valueitem_t valuetable, int myerrno, size_t *nbytes)
{
@@ -1354,6 +1370,7 @@ do_format (estream_printf_out_t outfnc, void *outfncarg,
int argidx = 0; /* Only used for assertion. */
size_t n;
value_t value;
+ int string_no = 0; /* Number of processed "%s" args. */
s = format;
while ( *s )
@@ -1443,8 +1460,12 @@ do_format (estream_printf_out_t outfnc, void *outfncarg,
rc = pr_char (outfnc, outfncarg, arg, value, nbytes);
break;
case CONSPEC_STRING:
+ rc = pr_string (outfnc, outfncarg, arg, value, nbytes,
+ sf, sfvalue, string_no++);
+ break;
case CONSPEC_STRERROR:
- rc = pr_string (outfnc, outfncarg, arg, value, nbytes);
+ rc = pr_string (outfnc, outfncarg, arg, value, nbytes,
+ NULL, NULL, 0);
break;
case CONSPEC_POINTER:
rc = pr_pointer (outfnc, outfncarg, arg, value, nbytes);
@@ -1478,6 +1499,7 @@ do_format (estream_printf_out_t outfnc, void *outfncarg,
int
_gpgrt_estream_format (estream_printf_out_t outfnc,
void *outfncarg,
+ gpgrt_string_filter_t sf, void *sfvalue,
const char *format, va_list vaargs)
{
/* Buffer to hold the argspecs and a pointer to it.*/
@@ -1489,10 +1511,10 @@ _gpgrt_estream_format (estream_printf_out_t outfnc,
struct valueitem_s valuetable_buffer[DEFAULT_MAX_VALUES];
valueitem_t valuetable = valuetable_buffer;
- int rc; /* Return code. */
+ int rc; /* Return code. */
size_t argidx; /* Used to index the argspecs array. */
size_t validx; /* Used to index the valuetable. */
- int max_pos;/* Highest argument position. */
+ int max_pos; /* Highest argument position. */
size_t nbytes = 0; /* Keep track of the number of bytes passed to
the output function. */
@@ -1606,7 +1628,7 @@ _gpgrt_estream_format (estream_printf_out_t outfnc,
/* fprintf (stderr, "%2d: vt=%d\n", validx, valuetable[validx].vt); */
/* Everything has been collected, go ahead with the formatting. */
- rc = do_format (outfnc, outfncarg, format,
+ rc = do_format (outfnc, outfncarg, sf, sfvalue, format,
argspecs, argspecs_len, valuetable, myerrno, &nbytes);
goto leave;
@@ -1646,7 +1668,8 @@ _gpgrt_estream_printf (const char *format, ...)
va_list arg_ptr;
va_start (arg_ptr, format);
- rc = _gpgrt_estream_format (plain_stdio_out, stderr, format, arg_ptr);
+ rc = _gpgrt_estream_format (plain_stdio_out, stderr, NULL, NULL,
+ format, arg_ptr);
va_end (arg_ptr);
return rc;
@@ -1660,7 +1683,8 @@ _gpgrt_estream_fprintf (FILE *fp, const char *format, ...)
va_list arg_ptr;
va_start (arg_ptr, format);
- rc = _gpgrt_estream_format (plain_stdio_out, fp, format, arg_ptr);
+ rc = _gpgrt_estream_format (plain_stdio_out, fp, NULL, NULL,
+ format, arg_ptr);
va_end (arg_ptr);
return rc;
@@ -1670,7 +1694,8 @@ _gpgrt_estream_fprintf (FILE *fp, const char *format, ...)
int
_gpgrt_estream_vfprintf (FILE *fp, const char *format, va_list arg_ptr)
{
- return _gpgrt_estream_format (plain_stdio_out, fp, format, arg_ptr);
+ return _gpgrt_estream_format (plain_stdio_out, fp, NULL, NULL,
+ format, arg_ptr);
}
@@ -1725,7 +1750,8 @@ _gpgrt_estream_vsnprintf (char *buf, size_t bufsize,
parm.count = 0;
parm.used = 0;
parm.buffer = bufsize?buf:NULL;
- rc = _gpgrt_estream_format (fixed_buffer_out, &parm, format, arg_ptr);
+ rc = _gpgrt_estream_format (fixed_buffer_out, &parm, NULL, NULL,
+ format, arg_ptr);
if (!rc)
rc = fixed_buffer_out (&parm, "", 1); /* Print terminating Nul. */
if (rc == -1)
@@ -1820,7 +1846,8 @@ _gpgrt_estream_vasprintf (char **bufp, const char *format, va_list arg_ptr)
return -1;
}
- rc = _gpgrt_estream_format (dynamic_buffer_out, &parm, format, arg_ptr);
+ rc = _gpgrt_estream_format (dynamic_buffer_out, &parm, NULL, NULL,
+ format, arg_ptr);
if (!rc)
rc = dynamic_buffer_out (&parm, "", 1); /* Print terminating Nul. */
/* Fixme: Should we shrink the resulting buffer? */
diff --git a/src/estream-printf.h b/src/estream-printf.h
index ae303a7..69c7052 100644
--- a/src/estream-printf.h
+++ b/src/estream-printf.h
@@ -124,8 +124,10 @@ typedef int (*estream_printf_out_t)
(void *outfncarg, const char *buf, size_t buflen);
int _gpgrt_estream_format (estream_printf_out_t outfnc, void *outfncarg,
+ char *(*string_filter)(const char *s,int n,void*st),
+ void *string_filter_state,
const char *format, va_list vaargs)
- _ESTREAM_GCC_A_PRINTF(3,0);
+ _ESTREAM_GCC_A_PRINTF(5,0);
int _gpgrt_estream_printf (const char *format, ...)
_ESTREAM_GCC_A_PRINTF(1,2);
int _gpgrt_estream_fprintf (FILE *fp, const char *format, ... )
diff --git a/src/estream.c b/src/estream.c
index 72e93e0..3645dfe 100644
--- a/src/estream.c
+++ b/src/estream.c
@@ -2982,12 +2982,13 @@ print_writer (void *outfncarg, const char *buf, size_t buflen)
/* The core of our printf function. This is called in locked state. */
static int
do_print_stream (estream_t _GPGRT__RESTRICT stream,
+ gpgrt_string_filter_t sf, void *sfvalue,
const char *_GPGRT__RESTRICT format, va_list ap)
{
int rc;
stream->intern->print_ntotal = 0;
- rc = _gpgrt_estream_format (print_writer, stream, format, ap);
+ rc = _gpgrt_estream_format (print_writer, stream, sf, sfvalue, format, ap);
if (rc)
return -1;
return (int)stream->intern->print_ntotal;
@@ -4444,22 +4445,24 @@ _gpgrt_read_line (estream_t stream,
int
_gpgrt_vfprintf_unlocked (estream_t _GPGRT__RESTRICT stream,
+ gpgrt_string_filter_t sf, void *sfvalue,
const char *_GPGRT__RESTRICT format,
va_list ap)
{
- return do_print_stream (stream, format, ap);
+ return do_print_stream (stream, sf, sfvalue, format, ap);
}
int
_gpgrt_vfprintf (estream_t _GPGRT__RESTRICT stream,
+ gpgrt_string_filter_t sf, void *sfvalue,
const char *_GPGRT__RESTRICT format,
va_list ap)
{
int ret;
lock_stream (stream);
- ret = do_print_stream (stream, format, ap);
+ ret = do_print_stream (stream, sf, sfvalue, format, ap);
unlock_stream (stream);
return ret;
@@ -4474,7 +4477,7 @@ _gpgrt_fprintf_unlocked (estream_t _GPGRT__RESTRICT stream,
va_list ap;
va_start (ap, format);
- ret = do_print_stream (stream, format, ap);
+ ret = do_print_stream (stream, NULL, NULL, format, ap);
va_end (ap);
return ret;
@@ -4490,7 +4493,7 @@ _gpgrt_fprintf (estream_t _GPGRT__RESTRICT stream,
va_list ap;
va_start (ap, format);
lock_stream (stream);
- ret = do_print_stream (stream, format, ap);
+ ret = do_print_stream (stream, NULL, NULL, format, ap);
unlock_stream (stream);
va_end (ap);
diff --git a/src/gpg-error.def.in b/src/gpg-error.def.in
index 9b522ea..f03a7e3 100644
--- a/src/gpg-error.def.in
+++ b/src/gpg-error.def.in
@@ -221,5 +221,8 @@ EXPORTS
gpgrt_cmp_version @169
gpgrt_ftruncate @170
+ gpgrt_fprintf_sf @171
+ gpgrt_fprintf_sf_unlocked @172
+
;; end of file with public symbols for Windows.
diff --git a/src/gpg-error.h.in b/src/gpg-error.h.in
index c0ac0eb..cb32c48 100644
--- a/src/gpg-error.h.in
+++ b/src/gpg-error.h.in
@@ -644,6 +644,11 @@ typedef struct _gpgrt_poll_s gpgrt_poll_t;
typedef struct _gpgrt_poll_s es_poll_t;
#endif
+/* The type of the string filter function as used by fprintf_sf et al. */
+typedef char *(*gpgrt_string_filter_t) (const char *s, int n, void *opaque);
+
+
+
gpgrt_stream_t gpgrt_fopen (const char *_GPGRT__RESTRICT path,
const char *_GPGRT__RESTRICT mode);
gpgrt_stream_t gpgrt_mopen (void *_GPGRT__RESTRICT data,
@@ -783,6 +788,15 @@ int gpgrt_fprintf_unlocked (gpgrt_stream_t _GPGRT__RESTRICT stream,
const char *_GPGRT__RESTRICT format, ...)
GPGRT_ATTR_PRINTF(2,3);
+int gpgrt_fprintf_sf (gpgrt_stream_t _GPGRT__RESTRICT stream,
+ gpgrt_string_filter_t sf, void *sfvalue,
+ const char *_GPGRT__RESTRICT format,
+ ...) GPGRT_ATTR_PRINTF(4,5);
+int gpgrt_fprintf_sf_unlocked (gpgrt_stream_t _GPGRT__RESTRICT stream,
+ gpgrt_string_filter_t sf, void *sfvalue,
+ const char *_GPGRT__RESTRICT format,
+ ...) GPGRT_ATTR_PRINTF(4,5);
+
int gpgrt_printf (const char *_GPGRT__RESTRICT format, ...)
GPGRT_ATTR_PRINTF(1,2);
int gpgrt_printf_unlocked (const char *_GPGRT__RESTRICT format, ...)
diff --git a/src/gpg-error.vers b/src/gpg-error.vers
index f9524d7..105e3bb 100644
--- a/src/gpg-error.vers
+++ b/src/gpg-error.vers
@@ -193,6 +193,9 @@ GPG_ERROR_1.0 {
gpgrt_cmp_version;
gpgrt_ftruncate;
+ gpgrt_fprintf_sf;
+ gpgrt_fprintf_sf_unlocked;
+
local:
*;
};
diff --git a/src/gpgrt-int.h b/src/gpgrt-int.h
index 09aad11..e326400 100644
--- a/src/gpgrt-int.h
+++ b/src/gpgrt-int.h
@@ -435,11 +435,13 @@ int _gpgrt_fprintf_unlocked (gpgrt_stream_t _GPGRT__RESTRICT stream,
GPGRT_ATTR_PRINTF(2,3);
int _gpgrt_vfprintf (gpgrt_stream_t _GPGRT__RESTRICT stream,
+ gpgrt_string_filter_t sf, void *sfvalue,
const char *_GPGRT__RESTRICT format, va_list ap)
- GPGRT_ATTR_PRINTF(2,0);
+ GPGRT_ATTR_PRINTF(4,0);
int _gpgrt_vfprintf_unlocked (gpgrt_stream_t _GPGRT__RESTRICT stream,
+ gpgrt_string_filter_t sf, void *sfvalue,
const char *_GPGRT__RESTRICT format, va_list ap)
- GPGRT_ATTR_PRINTF(2,0);
+ GPGRT_ATTR_PRINTF(4,0);
int _gpgrt_setvbuf (gpgrt_stream_t _GPGRT__RESTRICT stream,
char *_GPGRT__RESTRICT buf, int mode, size_t size);
diff --git a/src/logging.c b/src/logging.c
index d01f974..51e1362 100644
--- a/src/logging.c
+++ b/src/logging.c
@@ -839,7 +839,7 @@ _gpgrt_logv_internal (int level, int ignore_arg_ptr, const char *extrastring,
}
else
{
- rc = _gpgrt_vfprintf_unlocked (logstream, fmt, arg_ptr);
+ rc = _gpgrt_vfprintf_unlocked (logstream, NULL, NULL, fmt, arg_ptr);
if (rc > 0)
length += rc;
}
diff --git a/src/visibility.c b/src/visibility.c
index 1947111..573a5a4 100644
--- a/src/visibility.c
+++ b/src/visibility.c
@@ -515,7 +515,7 @@ gpgrt_vfprintf (estream_t _GPGRT__RESTRICT stream,
const char *_GPGRT__RESTRICT format,
va_list ap)
{
- return _gpgrt_vfprintf (stream, format, ap);
+ return _gpgrt_vfprintf (stream, NULL, NULL, format, ap);
}
int
@@ -523,7 +523,7 @@ gpgrt_vfprintf_unlocked (estream_t _GPGRT__RESTRICT stream,
const char *_GPGRT__RESTRICT format,
va_list ap)
{
- return _gpgrt_vfprintf_unlocked (stream, format, ap);
+ return _gpgrt_vfprintf_unlocked (stream, NULL, NULL, format, ap);
}
int
@@ -533,7 +533,7 @@ gpgrt_printf (const char *_GPGRT__RESTRICT format, ...)
int rc;
va_start (ap, format);
- rc = _gpgrt_vfprintf (es_stdout, format, ap);
+ rc = _gpgrt_vfprintf (es_stdout, NULL, NULL, format, ap);
va_end (ap);
return rc;
@@ -546,7 +546,7 @@ gpgrt_printf_unlocked (const char *_GPGRT__RESTRICT format, ...)
int rc;
va_start (ap, format);
- rc = _gpgrt_vfprintf_unlocked (es_stdout, format, ap);
+ rc = _gpgrt_vfprintf_unlocked (es_stdout, NULL, NULL, format, ap);
va_end (ap);
return rc;
@@ -560,7 +560,7 @@ gpgrt_fprintf (estream_t _GPGRT__RESTRICT stream,
int rc;
va_start (ap, format);
- rc = _gpgrt_vfprintf (stream, format, ap);
+ rc = _gpgrt_vfprintf (stream, NULL, NULL, format, ap);
va_end (ap);
return rc;
@@ -574,7 +574,37 @@ gpgrt_fprintf_unlocked (estream_t _GPGRT__RESTRICT stream,
int rc;
va_start (ap, format);
- rc = _gpgrt_vfprintf_unlocked (stream, format, ap);
+ rc = _gpgrt_vfprintf_unlocked (stream, NULL, NULL, format, ap);
+ va_end (ap);
+
+ return rc;
+}
+
+int
+gpgrt_fprintf_sf (estream_t _GPGRT__RESTRICT stream,
+ gpgrt_string_filter_t sf, void *sfvalue,
+ const char *_GPGRT__RESTRICT format, ...)
+{
+ va_list ap;
+ int rc;
+
+ va_start (ap, format);
+ rc = _gpgrt_vfprintf (stream, sf, sfvalue, format, ap);
+ va_end (ap);
+
+ return rc;
+}
+
+int
+gpgrt_fprintf_sf_unlocked (estream_t _GPGRT__RESTRICT stream,
+ gpgrt_string_filter_t sf, void *sfvalue,
+ const char *_GPGRT__RESTRICT format, ...)
+{
+ va_list ap;
+ int rc;
+
+ va_start (ap, format);
+ rc = _gpgrt_vfprintf_unlocked (stream, sf, sfvalue, format, ap);
va_end (ap);
return rc;
diff --git a/src/visibility.h b/src/visibility.h
index 46383c7..2dde522 100644
--- a/src/visibility.h
+++ b/src/visibility.h
@@ -121,6 +121,8 @@ MARK_VISIBLE (gpgrt_getline)
MARK_VISIBLE (gpgrt_read_line)
MARK_VISIBLE (gpgrt_fprintf)
MARK_VISIBLE (gpgrt_fprintf_unlocked)
+MARK_VISIBLE (gpgrt_fprintf_sf)
+MARK_VISIBLE (gpgrt_fprintf_sf_unlocked)
MARK_VISIBLE (gpgrt_printf)
MARK_VISIBLE (gpgrt_printf_unlocked)
MARK_VISIBLE (gpgrt_vfprintf)
@@ -296,6 +298,8 @@ MARK_VISIBLE (gpgrt_cmp_version);
#define gpgrt_read_line _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_fprintf _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_fprintf_unlocked _gpgrt_USE_UNDERSCORED_FUNCTION
+#define gpgrt_fprintf_sf _gpgrt_USE_UNDERSCORED_FUNCTION
+#define gpgrt_fprintf_sf_unlocked _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_printf _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_printf_unlocked _gpgrt_USE_UNDERSCORED_FUNCTION
#define gpgrt_vfprintf _gpgrt_USE_UNDERSCORED_FUNCTION
diff --git a/tests/t-printf.c b/tests/t-printf.c
index 7fba012..1e4d393 100644
--- a/tests/t-printf.c
+++ b/tests/t-printf.c
@@ -40,6 +40,49 @@
static char *one_test_buf1;
static int one_test_rc1;
+
+
+
+/* Read all data from STREAM into a new malloced buffer and return
+ * that buffer. The buffer is always 0 terminated. Either returns a
+ * string or dies. The stream will be trunctaed to zero. */
+static char *
+stream_to_string (gpgrt_stream_t stream)
+{
+#define NCHUNK 1024
+ char *buffer;
+ size_t bufsize, buflen;
+ size_t nread;
+
+ gpgrt_rewind (stream);
+
+ buffer = NULL;
+ buflen = bufsize = 0;
+ do
+ {
+ bufsize += NCHUNK;
+ buffer = realloc (buffer, bufsize+1);
+ if (!buffer)
+ die ("malloc failed at line %d\n", __LINE__);
+
+ nread = gpgrt_fread (buffer + buflen, 1, NCHUNK, stream);
+ if (nread < NCHUNK && gpgrt_ferror (stream))
+ die ("fread failed at line %d: %s\n", __LINE__, strerror (errno));
+ buflen += nread;
+ }
+ while (nread == NCHUNK);
+ buffer[nread] = 0;
+
+ if (strlen (buffer) != buflen)
+ fail ("stream_to_string detected an embedded nul");
+
+ gpgrt_ftruncate (stream, 0);
+ return buffer;
+#undef NCHUNK
+}
+
+
+
static void
one_test_x0 (const char *format, ...)
{
@@ -375,6 +418,74 @@ check_snprintf (void)
}
+struct sfstate_s
+{
+ char *last_result;
+};
+
+static char *
+string_filter (const char *string, int no, void *opaque)
+{
+ struct sfstate_s *state = opaque;
+
+ free (state->last_result);
+ if (no == -1)
+ {
+ state->last_result = NULL;
+ return NULL;
+ }
+ if (no == 3)
+ state->last_result = NULL;
+ else
+ state->last_result = strdup (string? string : "[==>Niente<==]");
+
+ return state->last_result;
+}
+
+
+static void
+check_fprintf_sf (void)
+{
+ volatile char *nullptr = NULL; /* Avoid compiler warning. */
+ struct sfstate_s sfstate = {NULL};
+ gpgrt_stream_t stream;
+ const char *expect;
+ char *result;
+
+ stream = gpgrt_fopenmem (0, "w+b");
+ if (!stream)
+ die ("fopenmem failed at line %d\n", __LINE__);
+
+ gpgrt_fprintf_sf (stream, string_filter, &sfstate,
+ "%s a=%d b=%s c=%d d=%.8s null=%s\n",
+ nullptr, 1, "foo\x01 bar", 2,
+ "a longer string", nullptr);
+ expect = "[==>Niente<==] a=1 b=foo\x01 bar c=2 d=a longer null=(null)\n";
+ result = stream_to_string (stream);
+ if (strcmp (result, expect))
+ {
+ show ("expect: '%s'\n", expect);
+ show ("result: '%s'\n", result);
+ fail ("fprintf_sf failed at %d\n", __LINE__);
+ }
+ free (result);
+
+ gpgrt_fprintf_sf (stream, string_filter, &sfstate,
+ "a=%d b=%s c=%d d=%.8s e=%s\n",
+ 1, "foo\n bar", 2, nullptr, "");
+ expect = "a=1 b=foo\n bar c=2 d=[==>Nien e=\n";
+ result = stream_to_string (stream);
+ if (strcmp (result, expect))
+ {
+ show ("expect: '%s'\n", expect);
+ show ("result: '%s'\n", result);
+ fail ("fprintf_sf failed at %d\n", __LINE__);
+ }
+ free (result);
+
+ gpgrt_fclose (stream);
+}
+
int
main (int argc, char **argv)
@@ -420,6 +531,7 @@ main (int argc, char **argv)
run_tests ();
check_snprintf ();
+ check_fprintf_sf ();
#ifdef __GLIBC__
return !!errorcount;