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 The \fBcurl_maprintf\fP and \fBcurl_mvaprintf\fP functions return a pointer to
a newly allocated string, or NULL if it failed. a newly allocated string, or NULL if it failed.
All other functions return the number of characters they actually All other functions return the number of characters actually printed
outputted. Note that this differs from how the POSIX versions of these (excluding the null byte used to end output to strings). Note that this
functions work. sometimes differ from how the POSIX versions of these functions work.
.SH "SEE ALSO" .SH "SEE ALSO"
.BR printf "(3), " sprintf "(3), " fprintf "(3), " vprintf "(3) " .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); retcode = dprintf_formatf(&info, addbyter, format, ap_save);
if((retcode != -1) && info.max) { if((retcode != -1) && info.max) {
/* we terminate this with a zero byte */ /* 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 */ /* we're at maximum, scrap the last letter */
info.buffer[-1] = 0; info.buffer[-1] = 0;
retcode--; /* don't count the nul byte */
}
else else
info.buffer[0] = 0; info.buffer[0] = 0;
} }

View File

@ -246,8 +246,7 @@ void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
size_t len; size_t len;
char buffer[MAXINFO + 2]; char buffer[MAXINFO + 2];
va_start(ap, fmt); va_start(ap, fmt);
(void)mvsnprintf(buffer, MAXINFO, fmt, ap); len = mvsnprintf(buffer, MAXINFO, fmt, ap);
len = strlen(buffer);
va_end(ap); va_end(ap);
buffer[len++] = '\n'; buffer[len++] = '\n';
buffer[len] = '\0'; buffer[len] = '\0';
@ -267,8 +266,7 @@ void Curl_failf(struct Curl_easy *data, const char *fmt, ...)
size_t len; size_t len;
char error[CURL_ERROR_SIZE + 2]; char error[CURL_ERROR_SIZE + 2];
va_start(ap, fmt); va_start(ap, fmt);
(void)mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap); len = mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap);
len = strlen(error);
if(data->set.errorbuffer && !data->state.errorbuf) { if(data->set.errorbuffer && !data->state.errorbuf) {
strcpy(data->set.errorbuffer, error); 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 * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * you should have received as part of this distribution. The terms
@ -1541,8 +1541,9 @@ static int test_weird_arguments(void)
buf[0] = 0; buf[0] = 0;
rc = curl_msnprintf(buf, sizeof(buf), "%d, %.*1$d", 500, 1); rc = curl_msnprintf(buf, sizeof(buf), "%d, %.*1$d", 500, 1);
if(rc != 256) { if(rc != sizeof(buf) - 1) {
printf("curl_mprintf() returned %d and not 256!\n", rc); printf("curl_mprintf() returned %d and not %d!\n", rc,
sizeof(buf) - 1);
errors++; errors++;
} }
@ -1669,6 +1670,36 @@ static int test_float_formatting(void)
} }
/* !checksrc! enable LONGLINE */ /* !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 test(char *URL)
{ {
int errors = 0; int errors = 0;
@ -1702,6 +1733,8 @@ int test(char *URL)
errors += test_float_formatting(); errors += test_float_formatting();
errors += test_return_codes();
if(errors) if(errors)
return TEST_ERR_MAJOR_BAD; return TEST_ERR_MAJOR_BAD;
else 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 * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * you should have received as part of this distribution. The terms
@ -64,27 +64,27 @@ fail_unless(!strcmp(output, "012"), "wrong output");
/* negative width */ /* negative width */
rc = curl_msnprintf(output, 8, "%-8s", str); 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"); fail_unless(!strcmp(output, "bug "), "wrong output");
/* larger width that string length */ /* larger width that string length */
rc = curl_msnprintf(output, 8, "%8s", str); 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"); fail_unless(!strcmp(output, " bu"), "wrong output");
/* output a number in a limited output */ /* output a number in a limited output */
rc = curl_msnprintf(output, 4, "%d", 10240); 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"); fail_unless(!strcmp(output, "102"), "wrong output");
/* padded strings */ /* padded strings */
rc = curl_msnprintf(output, 16, "%8s%8s", str, str); 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"); fail_unless(!strcmp(output, " bug bu"), "wrong output");
/* padded numbers */ /* padded numbers */
rc = curl_msnprintf(output, 16, "%8d%8d", 1234, 5678); 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"); fail_unless(!strcmp(output, " 1234 567"), "wrong output");
UNITTEST_STOP 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 */ /* A string just long enough to not be truncated */
memset(input, '\0', sizeof(input)); memset(input, '\0', sizeof(input));
memset(input, 'A', 2048); memset(input, 'A', 2047);
Curl_infof(data, "%s", input); Curl_infof(data, "%s", input);
fail_unless(strlen(result) == 2048, "No truncation of infof input"); fail_unless(strlen(result) == 2048, "No truncation of infof input");
fail_unless(verify(result, input) == 0, "No truncation of infof input"); fail_unless(verify(result, input) == 0, "No truncation of infof input");