msnprintf: return number of printed characters excluding null byte

... even when the output is "capped" by the maximum length argument.

Clarified in the docs.

Closes #7361
This commit is contained in:
Daniel Stenberg 2021-07-07 14:51:17 +02:00
parent e7416cfd2b
commit 9053dbbf62
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
6 changed files with 51 additions and 18 deletions

View File

@ -246,8 +246,8 @@ not use them in new programs or projects.
The \fBcurl_maprintf\fP and \fBcurl_mvaprintf\fP functions return a pointer to
a newly allocated string, or NULL if it failed.
All other functions return the number of characters they actually
outputted. Note that this differs from how the POSIX versions of these
functions work.
All other functions return the number of characters actually printed
(excluding the null byte used to end output to strings). Note that this
sometimes differ from how the POSIX versions of these functions work.
.SH "SEE ALSO"
.BR printf "(3), " sprintf "(3), " fprintf "(3), " vprintf "(3) "

View File

@ -1017,9 +1017,11 @@ int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format,
retcode = dprintf_formatf(&info, addbyter, format, ap_save);
if((retcode != -1) && info.max) {
/* we terminate this with a zero byte */
if(info.max == info.length)
if(info.max == info.length) {
/* we're at maximum, scrap the last letter */
info.buffer[-1] = 0;
retcode--; /* don't count the nul byte */
}
else
info.buffer[0] = 0;
}

View File

@ -246,8 +246,7 @@ void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
size_t len;
char buffer[MAXINFO + 2];
va_start(ap, fmt);
(void)mvsnprintf(buffer, MAXINFO, fmt, ap);
len = strlen(buffer);
len = mvsnprintf(buffer, MAXINFO, fmt, ap);
va_end(ap);
buffer[len++] = '\n';
buffer[len] = '\0';
@ -267,8 +266,7 @@ void Curl_failf(struct Curl_easy *data, const char *fmt, ...)
size_t len;
char error[CURL_ERROR_SIZE + 2];
va_start(ap, fmt);
(void)mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap);
len = strlen(error);
len = mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap);
if(data->set.errorbuffer && !data->state.errorbuf) {
strcpy(data->set.errorbuffer, error);

View File

@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@ -1541,8 +1541,9 @@ static int test_weird_arguments(void)
buf[0] = 0;
rc = curl_msnprintf(buf, sizeof(buf), "%d, %.*1$d", 500, 1);
if(rc != 256) {
printf("curl_mprintf() returned %d and not 256!\n", rc);
if(rc != sizeof(buf) - 1) {
printf("curl_mprintf() returned %d and not %d!\n", rc,
sizeof(buf) - 1);
errors++;
}
@ -1669,6 +1670,36 @@ static int test_float_formatting(void)
}
/* !checksrc! enable LONGLINE */
static int test_return_codes(void)
{
char buf[128];
int rc;
rc = curl_msnprintf(buf, 100, "%d", 9999);
if(rc != 4)
return 1;
rc = curl_msnprintf(buf, 100, "%d", 99999);
if(rc != 5)
return 1;
/* returns the length excluding the nul byte */
rc = curl_msnprintf(buf, 5, "%d", 99999);
if(rc != 4)
return 1;
/* returns the length excluding the nul byte */
rc = curl_msnprintf(buf, 5, "%s", "helloooooooo");
if(rc != 4)
return 1;
/* returns the length excluding the nul byte */
rc = curl_msnprintf(buf, 6, "%s", "helloooooooo");
if(rc != 5)
return 1;
return 0;
}
int test(char *URL)
{
int errors = 0;
@ -1702,6 +1733,8 @@ int test(char *URL)
errors += test_float_formatting();
errors += test_return_codes();
if(errors)
return TEST_ERR_MAJOR_BAD;
else

View File

@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@ -64,27 +64,27 @@ fail_unless(!strcmp(output, "012"), "wrong output");
/* negative width */
rc = curl_msnprintf(output, 8, "%-8s", str);
fail_unless(rc == 8, "return code should be 8");
fail_unless(rc == 7, "return code should be 7");
fail_unless(!strcmp(output, "bug "), "wrong output");
/* larger width that string length */
rc = curl_msnprintf(output, 8, "%8s", str);
fail_unless(rc == 8, "return code should be 8");
fail_unless(rc == 7, "return code should be 7");
fail_unless(!strcmp(output, " bu"), "wrong output");
/* output a number in a limited output */
rc = curl_msnprintf(output, 4, "%d", 10240);
fail_unless(rc == 4, "return code should be 4");
fail_unless(rc == 3, "return code should be 3");
fail_unless(!strcmp(output, "102"), "wrong output");
/* padded strings */
rc = curl_msnprintf(output, 16, "%8s%8s", str, str);
fail_unless(rc == 16, "return code should be 16");
fail_unless(rc == 15, "return code should be 15");
fail_unless(!strcmp(output, " bug bu"), "wrong output");
/* padded numbers */
rc = curl_msnprintf(output, 16, "%8d%8d", 1234, 5678);
fail_unless(rc == 16, "return code should be 16");
fail_unless(rc == 15, "return code should be 15");
fail_unless(!strcmp(output, " 1234 567"), "wrong output");
UNITTEST_STOP

View File

@ -108,7 +108,7 @@ fail_unless(verify(result, "(nil)") == 0, "Passing NULL as string");
/* A string just long enough to not be truncated */
memset(input, '\0', sizeof(input));
memset(input, 'A', 2048);
memset(input, 'A', 2047);
Curl_infof(data, "%s", input);
fail_unless(strlen(result) == 2048, "No truncation of infof input");
fail_unless(verify(result, input) == 0, "No truncation of infof input");