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)
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.
On 9 April 2011 19:28, Ilmari Karonen nospam@vyznev.net 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 (Toolserver-l@lists.wikimedia.org) https://lists.wikimedia.org/mailman/listinfo/toolserver-l Posting guidelines for this list: https://wiki.toolserver.org/view/Mailing_list_etiquette
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
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?
-----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.
On 10 April 2011 00:09, River Tarnell river.tarnell@wikimedia.de 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 (Toolserver-l@lists.wikimedia.org) https://lists.wikimedia.org/mailman/listinfo/toolserver-l Posting guidelines for this list: https://wiki.toolserver.org/view/Mailing_list_etiquette
-----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.
On 11 April 2011 19:59, River Tarnell river.tarnell@wikimedia.de 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 (Toolserver-l@lists.wikimedia.org) https://lists.wikimedia.org/mailman/listinfo/toolserver-l Posting guidelines for this list: https://wiki.toolserver.org/view/Mailing_list_etiquette
toolserver-l@lists.wikimedia.org