vsprintf and vsnprintf [-Wformat-nonliteral] warning on Clang 5.0 - c

i have this chunk of code
static void err_doit(int errnoflag, int level, const char *fmt, va_list ap)
{
int errno_save;
unsigned long n;
char buf[MAXLINE];
errno_save = errno;
#ifdef HAVE_VSNPRINTF
vsnprintf(buf, sizeof(buf), fmt, ap); /* this is safe */
#else
vsprintf(buf ,fmt, ap); /* this is not safe */
#endif
n = strlen(buf);
if (errnoflag)
snprintf(buf + n, sizeof(buf) - n, ": %s", strerror(errno_save));
strcat(buf, "\n");
if (daemon_proc) {
syslog(level,"%s", buf);
} else {
fflush(stdout);
fputs(buf, stderr);
fflush(stderr);
}
return;
}
when i compile it (Clang 5.0.0 with -Weverything) i obtain those warnings:
Building C object lib/netutils/CMakeFiles/netutils.dir/error.c.o
/Users/User/Desktop/project.cmake/lib/netutils/error.c:98:16: warning: format string is not a string literal [-Wformat-nonliteral]
vsprintf(buf ,fmt, ap); /* this is not safe */
^~~
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/secure/_stdio.h:66:57: note: expanded from
macro 'vsprintf'__builtin___vsprintf_chk (str, 0, __darwin_obsz(str), format, ap)
^
the same thing happens with this other function vsnprintf(buf, sizeof(buf), fmt, ap);
how can i fix this warning?
Thanks

Apparently the solution is to tell Clang that your vsnprintf is called within a function that implements the behaviour of the printf family of functions by, well, calling vsnprintf. You do this with an attribute, as described here:
__attribute__((__format__ (__printf__, 3, 0)))
static void err_doit(int errnoflag, int level, const char *fmt, va_list ap)
{
...
}
This checks whether the format string is a literal on the calling function. As err_doit takes already a va_list, you should specify the format on the functions that call it, too. The second number, which is 0 here, should then be the argument index of the variadic argument, .... See also this discussion.
Function attributes are a non-standard extension of gcc, which is also implemented for Clang. If you want to keep the code compiler independent, you should wrap the attributes in a macro that hides it for compilers that don't know attributes.
(Disclaimer: I couldn't check this with Clang, but it works for gcc. Edit: fixed wrong argument index in example)

Related

How to prevent GCC from compiling error-prone vsnprintf?

Is it possible to detect at compile time that the fourth argument of vsnprintf() consists of fewer than required by the third argument of vsnprintf()? I mean:
#include <stdio.h>
#include <stdarg.h>
void foo(const char* format, ...)
{
char buffer[256];
va_list args;
va_start (args, format);
vsnprintf(buffer, 256, format, args); // args may points to fewer arguments than expected in the format
va_end (args);
}
int main ()
{
foo("%d %d", 1); // the format requires two arguments, but only one is provided, so a warning from the compiler is expected here
return 0;
}
gcc -Wall main.c compiles the above without warnings
Apply an attribute to foo.
__attribute__((__format__(__printf__, 1, 2)))
void foo(const char* format, ...) {
Compile with -Werror=format.

How to use parameters const char *format in custom printf

I'm looking for a function that print log information, in order to debug my program.
This function must look at a boolean and decide if write the log in a file or to the console.
int useFile;
int log_informations( ? )
{
if(useFile)
{
// open/close FILE *pf omitted
fprintf(pf, ? );
}
else
{
printf( ? );
}
return 0;
}
int main()
{
int answer = 42;
log_informations("The answer is %d.", answer);
return 0;
}
Can you help me with the parameter? I did not found any reference.
NB: In this question I made things clear and simpler as they were in my context, so I do not need workaround but, possibly, a simple answer.
Thank you in advance ;)
As other mentioned, you don't need to actually write a function. A macro could help here:
#define LOG(fmt, ...) fprintf(logToFile ? fileLogger : stdout, fmt, ##__VA_ARGS__)
To answer your question you can look at how printf and vprintf work:
int printf(const char *format, ...);
int vprintf(const char *format, va_list arg);
printf is a variadic function and you want to provide your own version that wraps vprintf and vfprintf.
#include <stdarg.h> // needed for va_list, va_start, and va_end.
int my_printf(const char *format, ...)
{
va_list ap;
int ret;
va_start(ap, format);
if (useFile) ret = vfprintf(pf, format, ap);
else ret = vprintf(format, ap);
va_end(ap);
return ret;
}
EDIT: as #phuclv mentioned, if you want to include file, line, and/or function information in your log a macro is the only way. For example, for appending file and line information you could do something like this:
#define LOG(fmt, ...) fprintf(logToFile ? fileLogger : stdout, __FILE__ ":" STRINGIFY(__LINE__) " " fmt, ##__VA_ARGS__)
#define STRINGIFY_HELPER(x) #x
#define STRINGIFY(x) STRINGIFY_HELPER(x)
You need STRINGIGY for __LINE__ because it is an int.

Create a printf function with custom prefix

I'm trying to create a printf function that print like this
[INFO] whatever 123
va_args works but I don't know how to add a prefix. At least the following code won't do
#include <stdio.h>
#include <stdarg.h>
void myprintf (char *fmt, ...)
{
va_list argp;
va_start (argp, fmt);
vfprintf (stdout, "[INFO] " fmt, argp);
va_end (argp);
}
int main (int argc , char **argv)
{
myprintf ("arg count is %d\n", argc);
return 0;
}
Any ideas?
"[INFO] " fmt
This code won't work. The "string pasting" behavior you're trying to use here is a preprocessor behavior, not a C operator. It can only be used on string constants -- not variables.
The easiest way of getting the behavior you want here will be to simply call printf twice:
printf("[INFO] ");
va_start(argp, fmt);
vfprintf(stdout, fmt, argp);
va_end(argp);
More difficult approaches which you may want to consider include:
Define myprintf() as a macro instead of a function so that it can use string pasting on the format argument.
Copy "[INFO] " and fmt into a temporary buffer and use that as a formatting string.
Copy "[INFO] " into a temporary buffer, use vsnprintf() to append the output to the buffer, then output that.

following code crashes in 64 bit system

Following program crashes in 64 bit system. This code is working fine in 32 bit system
I am using centOS 32 bit and centOS 64 bit sytems..
what will be the reason for this ?
#if 1
if (1)
{
memset(message1, '\0', sizeof(message1));
vsprintf(message1, format, vAList);
}
#endif
if (1)
{
//PROGRAM CRASHES in the following line IF AM PUTTING #if 1 in the above code as i did
val = vfprintf(logFile, format,vAList);
}
when program reaches val = vfprintf(logFile,format,vAList);
here it crashes in 64 bit system..
You can't use a variadic argument list twice. vfprintf "uses up" the arguments and the next call probably tries to access memory after the list. In the words of the man page:
These functions [v*printf] do not call the va_end macro. Consequently, the value of ap is undefined after the call. The application should call va_end(ap) itself afterwards.
If your format requires dereferencing, like the %s format, chances are that your program crashes.
You can solve that in two ways: Either wrap all calls to vfprintf in their own va_start and va_end:
int f(const char *fmt, ...)
{
va_list va;
va_start(va, fmt);
vprintf(fmt, va);
va_end(va);
va_start(va, fmt);
vprintf(fmt, va);
va_end(va);
return 0;
}
Or make a copy of the variadic list:
int f(const char *fmt, ...)
{
va_list va, vb;
va_start(va, fmt);
va_copy(vb, va);
vprintf(fmt, va);
vprintf(fmt, vb);
va_end(va);
va_end(vb);
return 0;
}
(You must make the copy before the list you copy from has been used.)
Edit: Forgot to clean up the copied va lis, vb in the second example. Fixed now.

How can I use the va_list correctly?

I want to have something like a cross platform snprintf function, so I'm trying to use this (perhaps there are other solutions, but I'm wondering exactly that):
void string_print(char *str, size_t size, const char *format, ...) {
va_list args;
va_start(args, format);
#ifdef _WIN32
sprintf_s(str, size, format, args);
#else
snprintf(str, size, format, args);
#endif
va_end(args);
}
Example of usage:
// timeStepNumber == 1
char fileName[40];
string_print(fileName, 40, "Flow%d.dat", timeStepNumber);
But in this case I have fileName == "Flow-14843.dat", although va_arg(args, int) == 1. Can anybody explain, what maybe wrong in the string_print function?
You need to use vsnprintf/vsnprintf_s functions with vararg lists.
vsnprintf(str, size, format, args);
As indicated by imbtfab, use vsnprintf() in place of snprintf() and _vsnprintf() in place of sprintf_s.

Resources