I have the following functions:
void raiseError(const char *msg, ...)
{
va_list ap;
va_start(ap, msg); // use variable arg list
int size = vsnprintf(nullptr, 0, msg, ap);
std::vector<char> s(size+1, 0);
vsnprintf(s.data(), size, msg, ap);
va_end(ap);
errorString = std::string(s.data()));
}
When I call
raiseError("File not found in <%s> : <%s>", "a", "b" );
The first vsnprintf call (which computes the final string size) returns the correct value of 27. But the final string is:
"File not found in <**A*> :"
Where the '*' characters are random across program launches.
Also, the program works in MinGW, the issue is only seen with linux gcc.
What is wrong in my code?
You can't use ap more than once. Look into using va_copy.
Related
I have an old function which I can't change the API
void TraceMsg(const char* fmt, ...)
{
if (!m_MessageFunctions[TraceLevel]) return;
char msgBuffer[MAX_LOG_MSG];
va_list argList;
va_start(argList, fmt);
vsnprintf(msgBuffer, MAX_LOG_MSG, fmt, argList);
va_end(argList);
m_MessageFunctions[TraceLevel](msgBuffer);
}
MAX_LOG_MSG = 2048
I got into a phase where I would like to allocate more space for the messages for the logger in a dynamic way
I have read this article: https://code-examples.net/en/q/4904e5
and changed my code into:
void TraceMsg(const char* fmt, ...)
{
if (!m_MessageFunctions[TraceLevel]) return;
va_list argList;
va_start(argList, fmt);
size_t size = vsnprintf(NULL, 0,fmt, argList);
char* msgBuffer = new char[size];
vsnprintf(msgBuffer, size, fmt, argList);
va_end(argList);
m_MessageFunctions[TraceLevel](msgBuffer);
delete[] msgBuffer;
}
how ever I get wierd characters like
2022-05-03 12:13:20,939 INFO Make graph edge Bayer#LSC_1_2 ->Input#DeMux_LSC§§§§н
2022-05-03 12:13:20,939 INFO Make graph edge Bayer#RGB_IR_2_0 ->0#Mux_X2B_BP§§§§нннннњйн‚€нннннннннннннннннннннннннннннннннннн
Can you please help?
The return value of vsnprintf is
The number of characters that would have been written if n had been
sufficiently large, not counting the terminating null character.
So you need to add 1 to this to make room for the null terminator.
I have the following log function:
template<typename... Arguments>
void Log(const char* file, const int line, int level, const char* fmt, Arguments... args)
{
std::string formattedFile;
if (file)
{
boost::filesystem::path p(file);
formattedFile = p.filename().string();
}
std::string message{boost::str(boost::format("%1%:%2% [%3%] - %s") % formattedFile % line % m_uxid % fmt)};
__android_log_print(level, m_tag.c_str(), message.c_str(), args...);
}
This application is run on Android using NDK, so this is the logging system for that platform. The problem is that __android_log_print() fails to compile with:
error: format not a string literal and no format arguments [-Werror=format-security]
__android_log_print(level, m_tag.c_str(), message.c_str(), std::forward<Arguments>(args)...);
^
I'm not sure what this means. Am I not using the variadic template argument correctly?
Untrusted input into printf can be a security problem. Enforcing the format by using a string literal is one way of improving security
Turning warnings into errors will cause the build to fail so you are forced to address the warning.
GCC's warning options have this to say
-Werror:
Make all warnings into errors.
-Wformat-security:
Warn about uses of format functions that represent possible security problems.
At present, this warns about calls to printf and scanf functions where the format string is not a string literal and there are no format arguments
This may be a security hole if the format string came from untrusted input and contains %n.
What is generally advised is to create a std::string in your function and pass this with a %s format string literal to your logging function
__android_log_print(level, m_tag.c_str(), "%s", message.c_str());
Where message is built from processing args..., typically using something like boost::format or a std::stringstream.
If you want to use your provided fmt string and the variadic args, you can parse the arguments using a custom printf style function which produces a std::string
std::string va_string_printf(const char* format, va_list ap)
{
char stack_buf[256];
char* buf = stack_buf;
int buf_size = sizeof(stack_buf);
std::string out_str;
while(true)
{
va_list ap1;
va_copy(ap1, ap);
int min_buf_size = vsnprintf(buf, buf_size, format, ap1) + 1;
va_end(ap1);
if (min_buf_size > buf_size)
{
if (buf != stack_buf) // allocate a bigger buffer
delete[] buf;
buf = new char[min_buf_size];
buf_size = min_buf_size;
continue;
}
out_str = buf;
break;
}
if (buf != stack_buf)
delete[] buf;
return out_str;
}
std::string string_printf(const char* format, ...)
{
va_list ap;
va_start(ap, format);
std::string str = va_string_printf(format, ap);
va_end(ap);
return str;
}
Sorry, I know this is really basic, but I don't know how to search it the proper way, so here we go.
I'm trying to call MessageBoxA, and I want the message to replace '%s' with something else. Example:
MessageBoxA(0, TEXT("You have %s items"), "title", 0);
Can anyone help me? And once again, I know this is really basic, sorry.
You have to build the string yourself. In C++, this is typically done with std::ostringstream, e.g.:
#include <sstream>
...
std::ostringstream message;
message << "You have " << numItems << " items";
MessageBoxA(NULL, message.str().c_str(), "title", MB_OK);
In C, this is typically done with snprintf(3):
#include <stdio.h>
...
char buffer[256]; // Make sure this is big enough
snprintf(buffer, sizeof(buffer), "You have %d items", numItems);
MessageBoxA(NULL, buffer, "title", MB_OK);
You can write a utility function to build a std::string from a printf-style format:
#include <cstdio>
#include <cstdarg>
#include <string>
#include <vector>
std::string build_string(const char* fmt, ...) {
va_list args;
va_start(args, fmt);
size_t len = vsnprintf(NULL, 0, fmt, args);
va_end(args);
std::vector<char> vec(len + 1);
va_start(args, fmt);
vsnprintf(vec.data(), len + 1, fmt, args);
va_end(args);
return std::string(vec.begin(), vec.end() - 1);
}
With this function in place, you can create arbitrary strings and pass pointers to their contents as downward-arguments:
MessageBoxA(0, build_string("You have %d items", item_count).c_str(), "title", 0);
It has the advantage of simplicity (several lines of code using only stdio without dependency on iostreams), and has no arbitrary limits on the size of string.
For MessageBoxA it is:
char szBuf[120];
snprintf(szBuf,120, "You have %s items",nItemCount);
MessageBoxA(0, szBuf, "title", 0);
It is ugly, but it will fit your need.
Use boost::format.
In your example: MessageBoxA(0, (boost::format("You have %1 items") % "title").c_str(), 0);
One advantage is that you don't need to remember all those %s codes anymore, another is that you're no longer limited by the set of built-in flags.
The ( ).c_str() is needed because MessageBoxA is a C interface, not C++, and c_str() converts a C++ string to a C string.
is it possible to use vsnprintf to start printing from the exact value in the array?
For example, I would like to use vsnprintf to print from 25th character in the array.
Can I just this code?
va_list args;
#define length 100
char debug[length];
va_start(args, fmt);
vsnprintf(debug[25], length, fmt, args);
a_debug(devh,debug);
va_end(args);
Print from 25th character? You mean print into the buffer starting at the 25th byte position? Try this:
vsnprintf(debug + 25, length - 25, fmt, args);
The following code does not produce the expected output. Why?
wchar_t* wchar_t_printf_return(wchar_t* formatstring, ...){
va_list argp;
va_start(argp, formatstring);
int templen = 256;
templen = vsnwprintf(NULL, 0, formatstring, argp)+3;
wchar_t *buffer = (wchar_t *) malloc ((templen+1)*sizeof(wchar_t));
memset(buffer, 0, (templen+1)*sizeof(*buffer));
int retval;
while ((retval = vsnwprintf(buffer, templen, formatstring, argp)) == -1 || (retval >= (templen-1))){
templen = templen << 1;
buffer = (wchar_t *) realloc (buffer, (templen+1)*sizeof(wchar_t));
va_end(argp);
va_start(argp, formatstring);
}
va_end(argp);
buffer[templen] = L'\0';
return buffer;
}
int main(){
int i;
char *id = "2923BE84E16CD6AE529049F1F1BBE9EB";
wchar_t *val = wchar_t_printf_return(L"'%s'", id);
printf("%ls\n", val);
}
EDIT: to state more specifically, the printf in main should wrap the id in two single quotes thereby outputting: '2923BE84E16CD6AE529049F1F1BBE9EB'. The purpose of the main here is to illustrate the bug in the function, no more. The function is supposed to be an alternate of the printf family functions which return the result in a newly allocated buffer instead of a preexisting one. This is being run in cygwin compiled natively via gcc-3 with the -mno-cygwin option (aka mingw). Sorry for the confusion!
The %s specifier changes meaning depending on whether you are using a printf or wprintf family function. When used with a wprintf family function, the %s specifier indicates a wide string, but you're passing a narrow string. You need %hs to say "This is a narrow string."
(You seemed to be aware of this because you use %ls to print a wide string with a printf-family function, but you somehow forgot about it when going the other way.)
Try using %S for a translation, not %s.