Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unix to Windows: Alternative to vsnprintf to determine length?

I am currently converting the code of one of our Linux libraries to a Windows DLL.

Within this library I have a function which takes the last parameters in a printf-way (format string, then ellipsis). Within this function I use vsnprintf to format the supplied arguments. Since I want to know whether I can cram the final string into a small buffer or if I'd have to allocate memory for it, I am interested in determining the "would-be-length" of the formatted string.

To do this, I am currently using vsnprintf like this (made up example code, obviously):

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

void foo(const char* fmt, ...)
{
   int len = 0;
   va_list ap;

   va_start(ap, fmt);
   len = vsnprintf(0, 0, fmt, ap);
   printf("len = %d\n", len);
   va_end(ap);
}

int main(void)
{
   foo("12345%s", "67890");
   exit(0);
}

This usage is covered by the Open Group Base Specifications Issue 6:

vsnprintf(char *restrict s, size_t n, const char *restrict format, va_list ap)

The [...] vsnprintf() [...] functions shall be equivalent to [...] snprintf().

snprintf(char *restrict s, size_t n, const char *restrict format, ...)

If the value of n is zero on a call to snprintf(), nothing shall be written, the number of bytes that would have been written had n been sufficiently large excluding the terminating null shall be returned, and s may be a null pointer.

The problem arised as I was compiling this code on the Windows-System (Visual Studio 2010) with /analyze on. The compiler/analyzer gave me the following:

test.c(11) : warning C6309: Argument '1' is null: this does not adhere to function specification of 'vsnprintf'

test.c(11) : warning C6387: 'argument 1' might be '0': this does not adhere to the specification for the function 'vsnprintf': Lines: 7, 8, 10, 11

A quick look at the MSDN entry for vsnprintf gave me this:

If buffer or format is NULL, or if count is less than or equal to zero, these functions invoke the invalid parameter handler, as described in Parameter Validation. If execution is allowed to continue, these functions return -1.

Curious enough, the above sample works nonetheless on Windows "as expected" (i.e. it returns to me the count of the characters that would be written).

But since I don't want this to rely on something unspecified I'd like to know if there is a better, official way of achieving the same, without having to hope that this won't break in some future release.

Thanks for your time!

like image 926
lx. Avatar asked Dec 13 '11 11:12

lx.


2 Answers

Answer provided in comments by Hans Passant:

The documented _vscprintf provides this functionality on Windows, so relying on "unspecified behaviour" is not necessary.

like image 119
lx. Avatar answered Nov 17 '22 16:11

lx.


Take this example from the manual page of snprintf:

Here is how to allocate a buffer in order to fit your string.

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

char *
make_message(const char *fmt, ...)
{
    int n;
    int size = 100;     /* Guess we need no more than 100 bytes. */
    char *p, *np;
    va_list ap;

   if ((p = malloc(size)) == NULL)
        return NULL;

   while (1) {

       /* Try to print in the allocated space. */

       va_start(ap, fmt);
        n = vsnprintf(p, size, fmt, ap);
        va_end(ap);

       /* If that worked, return the string. */

       if (n > -1 && n < size)
            return p;

       /* Else try again with more space. */

       if (n > -1)    /* glibc 2.1 */
            size = n+1; /* precisely what is needed */
        else           /* glibc 2.0 */
            size *= 2;  /* twice the old size */

       if ((np = realloc (p, size)) == NULL) {
            free(p);
            return NULL;
        } else {
            p = np;
        }
    }
}
like image 3
INS Avatar answered Nov 17 '22 15:11

INS