c++ converting const char* to char* for long buffer - c++

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.

Related

Wrong vsnprintf output

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.

C++ string append formatted data

I have created my own string class in C++ (I didnt want to use 3rd party lib or std::string for some reasons).
Now I have an issue with appending formated string into my own. I have created this function:
void MyStringAnsi::AppendFormat(const char * str, ...)
{
va_list vl;
va_start(vl, str);
int newLength = static_cast<int>(this->length + 10 * strlen(str));
this->ResizeBuffer(this->length + newLength);
vsnprintf_s(this->str + this->length, newLength, newLength, str, vl);
va_end(vl);
this->length = static_cast<int>(strlen(this->str));
this->str[this->length] = '\0';
this->hashCode = UINT32_MAX;
}
Problem is with newLength of appended string. I can not calculate it, so I set it by some "magic" multiplier, but it is not enough.
Is this problem solvable (iterate all varagrs or change it to mething else)? I can use C++11 features, so maybe something there?
I call my code with
MyStringAnsi str = "xy";
str.AppendFormat("%s AND %d", someLongString, -50.7);
With gcc, you may use returned value of vsnprintf to know the wanted size.
For msvc, you have to increase size until vsnprintf doesn't return -1.

Forwarding variadic template parameter to printf-like function

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;
}

vsnprintf from the exact value in array

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);

Trying to combine two similar printf style debug messages in to a single function

I have two printf style debug logging functions (DebuglogfA, DebuglogfB). Both operate the same way but one of the logging functions takes a logging level as a parameters and ignores low level debug messages.
Currently I duplicate the code for each of these functions but I would like DebuglogfB to be able to call DebuglogfA if the debug level is high enough without having to create a temporary buffer in DebuglogfB.
void DebuglogfA( const char *lpszText, ...)
{
//Initialize variable argument list
va_list argList;
va_start(argList, lpszText);
char buffer[1024];
unsigned short length = snprintf_s(buffer, 1024, "[%d] ", CTime::GetCurrentTimeInSec() );
length += vsnprintf (buffer+length, 1024 - length, lpszText, argList );
LogSend( buffer, length );
}
void DebuglogfB ( const unsigned int level, const char *lpszText, ... )
{
if( level < 50 ) {
return; // To low to report.
}
//Initialize variable argument list
va_list argList;
va_start(argList, lpszText);
char buffer[1024];
unsigned short length = snprintf_s(buffer, 1024, "[%d] ", CTime::GetCurrentTimeInSec() );
length += vsnprintf (buffer+length, 1024 - length, lpszText, argList );
LogSend( buffer, length );
}
My question is:
How do I get function DebuglogfB to call DebuglogfA without creating a buffer for the message in DebuglogfB?
You could create a new function DebuglogfV which has const char *lpszText and va_list argList as parameters, and then let DebuglogfA and DebuglogfB call it to perform the actual logging.