Alternative C function to vasprintf() on Toolserver

classic Classic list List threaded Threaded
9 messages Options
Reply | Threaded
Open this post in threaded view
|

Alternative C function to vasprintf() on Toolserver

Andrew Dunbar
I've got a little program to index dump files that supports Windows
and Linux but it doesn't compile on the Toolserver with either cc or
gcc due to the lack of the function vasprintf(). It's a GNU extension
so I'm surprised it didn't work even with gcc.

Why doesn't the Toolserver gcc have it, and does anybody know of a workaround?

vasprintf() is a version of vsprintf() which writes to a memory buffer
of just the right size that it allocates and which the caller must
call free() on when done.

Andrew Dunbar (hippietrail)

_______________________________________________
Toolserver-l mailing list ([hidden email])
https://lists.wikimedia.org/mailman/listinfo/toolserver-l
Posting guidelines for this list: https://wiki.toolserver.org/view/Mailing_list_etiquette
Reply | Threaded
Open this post in threaded view
|

Re: Alternative C function to vasprintf() on Toolserver

Ilmari Karonen
On 04/09/2011 10:29 AM, Andrew Dunbar wrote:

> I've got a little program to index dump files that supports Windows
> and Linux but it doesn't compile on the Toolserver with either cc or
> gcc due to the lack of the function vasprintf(). It's a GNU extension
> so I'm surprised it didn't work even with gcc.
>
> Why doesn't the Toolserver gcc have it, and does anybody know of a workaround?
>
> vasprintf() is a version of vsprintf() which writes to a memory buffer
> of just the right size that it allocates and which the caller must
> call free() on when done.

You could try writing your own.  Off the top of my head (untested):

char *vasprintf (const char *format, va_list ap) {
        int len; char *buf;
        len = vsnprintf(NULL, 0, format, ap);  /* get needed size */
        if (len < 0) return NULL;
        buf = malloc(len + 1);  /* reserve 1 byte for trailing \0 */
        if (!len) return NULL;
        if (vsnprintf(buf, len + 1, format, ap) == len) return buf;
        free(buf);  /* something went wrong in second vsnprintf() */
        return NULL;
}

I think the vsnprintf(NULL, 0, ...) trick should work, although I
haven't tried it.  If it complains about the NULL, just use some valid
dummy pointer instead.  You could probably trade some memory for speed
in some cases by guessing and allocating some reasonable initial size
for the buffer before the first vsnprintf() call and extending it only
if the first guess wasn't long enough.

--
Ilmari Karonen

_______________________________________________
Toolserver-l mailing list ([hidden email])
https://lists.wikimedia.org/mailman/listinfo/toolserver-l
Posting guidelines for this list: https://wiki.toolserver.org/view/Mailing_list_etiquette
Reply | Threaded
Open this post in threaded view
|

Re: Alternative C function to vasprintf() on Toolserver

Andrew Dunbar
On 9 April 2011 19:28, Ilmari Karonen <[hidden email]> wrote:

> On 04/09/2011 10:29 AM, Andrew Dunbar wrote:
>> I've got a little program to index dump files that supports Windows
>> and Linux but it doesn't compile on the Toolserver with either cc or
>> gcc due to the lack of the function vasprintf(). It's a GNU extension
>> so I'm surprised it didn't work even with gcc.
>>
>> Why doesn't the Toolserver gcc have it, and does anybody know of a workaround?
>>
>> vasprintf() is a version of vsprintf() which writes to a memory buffer
>> of just the right size that it allocates and which the caller must
>> call free() on when done.
>
> You could try writing your own.  Off the top of my head (untested):
>
> char *vasprintf (const char *format, va_list ap) {
>        int len; char *buf;
>        len = vsnprintf(NULL, 0, format, ap);  /* get needed size */
>        if (len < 0) return NULL;
>        buf = malloc(len + 1);  /* reserve 1 byte for trailing \0 */
>        if (!len) return NULL;
>        if (vsnprintf(buf, len + 1, format, ap) == len) return buf;
>        free(buf);  /* something went wrong in second vsnprintf() */
>        return NULL;
> }
>
> I think the vsnprintf(NULL, 0, ...) trick should work, although I
> haven't tried it.  If it complains about the NULL, just use some valid
> dummy pointer instead.  You could probably trade some memory for speed
> in some cases by guessing and allocating some reasonable initial size
> for the buffer before the first vsnprintf() call and extending it only
> if the first guess wasn't long enough.

Aha thanks! I already use vsnprintf in the Windows version of the code
but since the gcc on my Ubuntu 10 doesn't have it I didn't expect it
to be on the Toolserver gcc.

Any ideas how I can set up the #ifdefs to detect that it's compiling
on the toolserver environment or that it has this function available?

Andrew Dunbar (hippietrail)

> --
> Ilmari Karonen
>
> _______________________________________________
> Toolserver-l mailing list ([hidden email])
> https://lists.wikimedia.org/mailman/listinfo/toolserver-l
> Posting guidelines for this list: https://wiki.toolserver.org/view/Mailing_list_etiquette
>

_______________________________________________
Toolserver-l mailing list ([hidden email])
https://lists.wikimedia.org/mailman/listinfo/toolserver-l
Posting guidelines for this list: https://wiki.toolserver.org/view/Mailing_list_etiquette
Reply | Threaded
Open this post in threaded view
|

Re: Alternative C function to vasprintf() on Toolserver

Platonides
In reply to this post by Andrew Dunbar
Andrew Dunbar wrote:
> I've got a little program to index dump files that supports Windows
> and Linux but it doesn't compile on the Toolserver with either cc or
> gcc due to the lack of the function vasprintf(). It's a GNU extension
> so I'm surprised it didn't work even with gcc.
>
> Why doesn't the Toolserver gcc have it, and does anybody know of a workaround?

Did you do
 #define _GNU_SOURCE
at the top of the source file?


_______________________________________________
Toolserver-l mailing list ([hidden email])
https://lists.wikimedia.org/mailman/listinfo/toolserver-l
Posting guidelines for this list: https://wiki.toolserver.org/view/Mailing_list_etiquette
Reply | Threaded
Open this post in threaded view
|

Re: Alternative C function to vasprintf() on Toolserver

River Tarnell-7
In reply to this post by Andrew Dunbar
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Andrew Dunbar:
> I've got a little program to index dump files that supports Windows
> and Linux but it doesn't compile on the Toolserver with either cc or
> gcc due to the lack of the function vasprintf(). It's a GNU extension
> so I'm surprised it didn't work even with gcc.
 
> Why doesn't the Toolserver gcc have it, and does anybody know of a workaround?

*printf() is not part of the compiler, but the C library (libc); GNU
glibc (used on Linux) provides it, while Solaris doesn't.  There's been
some discussion about it, but there are several incompatible asprintf()
interfaces around and it's not clear which one should be implemented.

Someone else already provided a version using snprintf, but you could
also use g_vasprintf() from glib, or the version from gettext's
libintl.h (which means you need to include <libintl.h> and link against
- -lasprintf, both of which live in /opt/ts/gettext).  I personally find
the snprintf solution the best and this is what I use in my own code.

        - river.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (SunOS)

iEYEARECAAYFAk2gaJ4ACgkQIXd7fCuc5vKziwCfTbDgK0nX32jK8iJcLWl278BK
HNwAnRFW8/BhMUKS7zr6RRBcPlqCeWfj
=31oq
-----END PGP SIGNATURE-----

_______________________________________________
Toolserver-l mailing list ([hidden email])
https://lists.wikimedia.org/mailman/listinfo/toolserver-l
Posting guidelines for this list: https://wiki.toolserver.org/view/Mailing_list_etiquette
Reply | Threaded
Open this post in threaded view
|

Re: Alternative C function to vasprintf() on Toolserver

Andrew Dunbar
On 10 April 2011 00:09, River Tarnell <[hidden email]> wrote:

> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> Andrew Dunbar:
>> I've got a little program to index dump files that supports Windows
>> and Linux but it doesn't compile on the Toolserver with either cc or
>> gcc due to the lack of the function vasprintf(). It's a GNU extension
>> so I'm surprised it didn't work even with gcc.
>
>> Why doesn't the Toolserver gcc have it, and does anybody know of a workaround?
>
> *printf() is not part of the compiler, but the C library (libc); GNU
> glibc (used on Linux) provides it, while Solaris doesn't.  There's been
> some discussion about it, but there are several incompatible asprintf()
> interfaces around and it's not clear which one should be implemented.
>
> Someone else already provided a version using snprintf, but you could
> also use g_vasprintf() from glib, or the version from gettext's
> libintl.h (which means you need to include <libintl.h> and link against
> - -lasprintf, both of which live in /opt/ts/gettext).  I personally find
> the snprintf solution the best and this is what I use in my own code.

Hmm currently I'm using _vscprintf / vsprintf on Windows/MSVC inside
#ifdef _WIN32
and vasprintf Ubuntu/gcc inside #else

But my C-fu is to puny to figure out what #ifdefs to use to detect
what combination of C compiler and libc I'm compiling under.
Apparently it's important with snprintf since some implementations
return "an unspecified return value less than 1" with size=0.

I don't want to have to use autoconf or swtich to a gcc on Windows.

Andrew Dunbar (hippietrail)


>        - river.
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.4.11 (SunOS)
>
> iEYEARECAAYFAk2gaJ4ACgkQIXd7fCuc5vKziwCfTbDgK0nX32jK8iJcLWl278BK
> HNwAnRFW8/BhMUKS7zr6RRBcPlqCeWfj
> =31oq
> -----END PGP SIGNATURE-----
>
> _______________________________________________
> Toolserver-l mailing list ([hidden email])
> https://lists.wikimedia.org/mailman/listinfo/toolserver-l
> Posting guidelines for this list: https://wiki.toolserver.org/view/Mailing_list_etiquette
>

_______________________________________________
Toolserver-l mailing list ([hidden email])
https://lists.wikimedia.org/mailman/listinfo/toolserver-l
Posting guidelines for this list: https://wiki.toolserver.org/view/Mailing_list_etiquette
Reply | Threaded
Open this post in threaded view
|

Re: Alternative C function to vasprintf() on Toolserver

River Tarnell-7
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Andrew Dunbar:
> Hmm currently I'm using _vscprintf / vsprintf on Windows/MSVC inside
> #ifdef _WIN32
> and vasprintf Ubuntu/gcc inside #else
 
> But my C-fu is to puny to figure out what #ifdefs to use to detect
> what combination of C compiler and libc I'm compiling under.

> Apparently it's important with snprintf since some implementations
> return "an unspecified return value less than 1" with size=0.
 
That's true, but no current Unix implementation should do that, since
the return value of snprintf() is standardised in the current POSIX
standard (IEEE 1003.1-2004) and in the C99 standard (ISO/IEC 9899:1999).

The following code should handle Windows and both old and new Unix,
although it's completely untested and comes with no warranty:

/* Compile with "-D_XOPEN_SOURCE=600 -std=c99" */
int
my_vasprintf(char **res, char const *fmt, va_list args)
{
int sz, r;
#ifdef _WIN32
        sz = _vscprintf(fmt, args);
#else
        sz = snprintf(NULL, 0, fmt, args);
#endif

#if defined(_WIN32) || (defined(__STDC__) && __STDC__ >= 199901L) \
                || (defined(_XOPEN_VERSION) && (_XOPEN_VERSION >= 600))
        if (sz < 0)
                return sz;
        if (sz >= 0) {
#else
        if (sz >= 1) {
#endif
                if ((*res = malloc(sz + 1)) == NULL)
                        return -1;

                if ((sz = sprintf(*res, fmt, args)) < 0) {
                        free(*res);
                        *res = NULL;
                }

                return sz;
        }
       
#define MAXLN 65535
        *res = NULL;
        for (sz = 128; sz <= MAXLN; sz *= 2) {
                if ((*res = realloc(*res, sz)) == NULL)
                        return -1;
                r = vsnprintf(*res, sz, fmt, args);
                if (r > 0 && r < sz)
                        return r;
        }
       
        errno = ENOMEM;

        if (*res) {
                free(*res);
                *res = NULL;
        }

        return -1;
}

Obviously, this will be slower than the modern implementation, and you
may want to tune the initial buffer size for the expected string length.

        - river.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (SunOS)

iEYEARECAAYFAk2i0QcACgkQIXd7fCuc5vI1ewCfTdlavg0rr56l7JzwAe5Oea9x
55sAnA4MIosJLfOnD/O3z/nkq0yqD5CE
=AGMe
-----END PGP SIGNATURE-----

_______________________________________________
Toolserver-l mailing list ([hidden email])
https://lists.wikimedia.org/mailman/listinfo/toolserver-l
Posting guidelines for this list: https://wiki.toolserver.org/view/Mailing_list_etiquette
Reply | Threaded
Open this post in threaded view
|

Re: Alternative C function to vasprintf() on Toolserver

Andrew Dunbar
On 11 April 2011 19:59, River Tarnell <[hidden email]> wrote:

> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> Andrew Dunbar:
>> Hmm currently I'm using _vscprintf / vsprintf on Windows/MSVC inside
>> #ifdef _WIN32
>> and vasprintf Ubuntu/gcc inside #else
>
>> But my C-fu is to puny to figure out what #ifdefs to use to detect
>> what combination of C compiler and libc I'm compiling under.
>
>> Apparently it's important with snprintf since some implementations
>> return "an unspecified return value less than 1" with size=0.
>
> That's true, but no current Unix implementation should do that, since
> the return value of snprintf() is standardised in the current POSIX
> standard (IEEE 1003.1-2004) and in the C99 standard (ISO/IEC 9899:1999).
>
> The following code should handle Windows and both old and new Unix,
> although it's completely untested and comes with no warranty:
>
> /* Compile with "-D_XOPEN_SOURCE=600 -std=c99" */
> int
> my_vasprintf(char **res, char const *fmt, va_list args)
> {
> int      sz, r;
> #ifdef _WIN32
>        sz = _vscprintf(fmt, args);
> #else
>        sz = snprintf(NULL, 0, fmt, args);
> #endif
>
> #if defined(_WIN32) || (defined(__STDC__) && __STDC__ >= 199901L) \
>                || (defined(_XOPEN_VERSION) && (_XOPEN_VERSION >= 600))
>        if (sz < 0)
>                return sz;
>        if (sz >= 0) {
> #else
>        if (sz >= 1) {
> #endif
>                if ((*res = malloc(sz + 1)) == NULL)
>                        return -1;
>
>                if ((sz = sprintf(*res, fmt, args)) < 0) {
>                        free(*res);
>                        *res = NULL;
>                }
>
>                return sz;
>        }
>
> #define MAXLN 65535
>        *res = NULL;
>        for (sz = 128; sz <= MAXLN; sz *= 2) {
>                if ((*res = realloc(*res, sz)) == NULL)
>                        return -1;
>                r = vsnprintf(*res, sz, fmt, args);
>                if (r > 0 && r < sz)
>                        return r;
>        }
>
>        errno = ENOMEM;
>
>        if (*res) {
>                free(*res);
>                *res = NULL;
>        }
>
>        return -1;
> }
>
> Obviously, this will be slower than the modern implementation, and you
> may want to tune the initial buffer size for the expected string length.

Thanks River, that's really helpful!

Andrew Dunbar (hippietrail)

>        - river.
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.4.11 (SunOS)
>
> iEYEARECAAYFAk2i0QcACgkQIXd7fCuc5vI1ewCfTdlavg0rr56l7JzwAe5Oea9x
> 55sAnA4MIosJLfOnD/O3z/nkq0yqD5CE
> =AGMe
> -----END PGP SIGNATURE-----
>
> _______________________________________________
> Toolserver-l mailing list ([hidden email])
> https://lists.wikimedia.org/mailman/listinfo/toolserver-l
> Posting guidelines for this list: https://wiki.toolserver.org/view/Mailing_list_etiquette
>

_______________________________________________
Toolserver-l mailing list ([hidden email])
https://lists.wikimedia.org/mailman/listinfo/toolserver-l
Posting guidelines for this list: https://wiki.toolserver.org/view/Mailing_list_etiquette
Reply | Threaded
Open this post in threaded view
|

Re: Alternative C function to vasprintf() on Toolserver

Peter Körner-4
In reply to this post by Ilmari Karonen
Am 09.04.2011 11:28, schrieb Ilmari Karonen:
> char *vasprintf (const char *format, va_list ap) {
> int len; char *buf;
> len = vsnprintf(NULL, 0, format, ap);  /* get needed size */
> if (len<  0) return NULL;
> buf = malloc(len + 1);  /* reserve 1 byte for trailing \0 */
> if (!len) return NULL;

shouldn't that line read
         if (!buf) return NULL;

> if (vsnprintf(buf, len + 1, format, ap) == len) return buf;
> free(buf);  /* something went wrong in second vsnprintf() */
> return NULL;
> }

Peter

_______________________________________________
Toolserver-l mailing list ([hidden email])
https://lists.wikimedia.org/mailman/listinfo/toolserver-l
Posting guidelines for this list: https://wiki.toolserver.org/view/Mailing_list_etiquette