So, while porting some code from older systems, I required `asprintf` and `vasprintf`. There is a bit of code that implements portable *printf/v*printf functions, but all I got from that were segfaults.
Hacked up these based on some code I found hidden away in MiscKit.
Not sure if these were previously documented anywhere public, or known about or whatnot, so putting these here in case they're of use to anyone:
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <strings.h>
#include <sys/types.h>
#include <objc/objc.h>
#include <objc/zone.h>
#include <streams/streams.h>
/* --------------------------------------------------------- */
/* Hacks on GNU x*alloc functions so they use NXZone. */
/* --------------------------------------------------------- */
inline
void *
xzonemalloc(NXZone *zone, size_t size)
{
register void *p = NULL;
if ((p = NXZoneMalloc(zone, size)) == NULL) {
perror("xzonemalloc");
exit(EXIT_FAILURE);
}
bzero(p, size);
return p;
}
inline
void *
xzonerealloc(NXZone *zone, void *ptr, size_t n)
{
if ((ptr = NXZoneRealloc(zone, ptr, n)) == NULL) {
perror("xzonerealloc");
exit(EXIT_FAILURE);
}
bzero(ptr, n);
return ptr;
}
inline
void *
xzonecalloc(NXZone *zone, size_t nelems, size_t elemsize)
{
register void *newmem = NULL;
newmem = NXZoneCalloc(zone,
nelems ? nelems : 1,
elemsize ? elemsize : 1);
if (newmem == NULL) {
perror("xzonecalloc");
exit(EXIT_FAILURE);
}
return newmem;
}
void
xzonefree(NXZone *zone, void *ptr)
{
if (ptr == NULL) {
return;
}
NXZoneFree(zone, ptr);
}
/* ... */
#if defined(xmalloc)
# undef xmalloc
#endif
#define xmalloc(__n) \
xzonemalloc(NXDefaultMallocZone(), __n)
#if defined(xrealloc)
# undef xrealloc
#endif
#define xrealloc(__p, __n) \
xzonerealloc(NXDefaultMallocZone(), __p, __n)
#if defined(xcalloc)
# undef xcalloc
#endif
#define xcalloc(__n, __e) \
xzonecalloc(NXDefaultMallocZone(), __n, __e)
#if defined(xfree)
# undef xfree
#endif
#define xfree(__p) \
xzonefree(NXDefaultMallocZone(), __p)
/* --------------------------------------------------------- */
/* NeXT port of `vasprintf`, using NXZone and NXVPrintf */
/* to automatically allocate a buffer large enough for the */
/* formatted string. */
/* --------------------------------------------------------- */
int
vasprintf(char **buf, const char *fmt, va_list args)
{
NXStream *stream = NULL;
int length = 0;
stream = NXOpenMemory(NULL, 0, NX_READWRITE);
NXVPrintf(stream, fmt, args);
NXFlush(stream);
NXSeek(stream, 0, NX_FROMEND);
length = NXTell(stream);
if (length == 0) {
return 0;
}
NXSeek(stream, 0, NX_FROMSTART);
/* Prefer xmalloc over NX_MALLOC due to error handling. */
*buf = xmalloc((length + 1) * sizeof(char));
NXRead(stream, *buf, length);
NXCloseMemory(stream, NX_FREEBUFFER);
buf[length] = '\0';
return length;
}
/* --------------------------------------------------------- */
/* NeXT version of `asprintf`. Just calls the `vasprintf` */
/* function. */
/* --------------------------------------------------------- */
int
asprintf(char **strp, const char *fmt, ...)
{
va_list ap;
size_t res = 0;
va_start(ap, fmt);
res = vasprintf(strp, fmt, ap);
va_end(ap);
return res;
}
/* --------------------------------------------------------- */
/* NeXT port of `vsnprintf`. Uses a similar mechanic to */
/* `vasprintf`, except the buffer is pre-allocated and */
/* there is a size check to ensure no overflow. */
/* --------------------------------------------------------- */
int
vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
{
NXStream *stream = NULL;
int length = 0;
stream = NXOpenMemory(NULL, 0, NX_READWRITE);
NXVPrintf(stream, fmt, args);
NXFlush(stream);
NXSeek(stream, 0, NX_FROMEND);
length = NXTell(stream);
if (length == 0) {
return 0;
}
if (length > size) {
length = size;
}
NXSeek(stream, 0, NX_FROMSTART);
NXRead(stream, buf, length);
NXCloseMemory(stream, NX_FREEBUFFER);
buf[length] = '\0';
return length;
}
/* --------------------------------------------------------- */
/* NeXT version of `snprintf`. Just calls the `vsnprintf` */
/* function. */
/* --------------------------------------------------------- */
int
snprintf(char *buf, size_t size, const char *fmt, ...)
{
va_list ap;
size_t res = 0;
va_start(ap, fmt);
res = vsnprintf(buf, size, fmt, ap);
va_end(ap);
return res;
}
Back a long time ago when I was fiddling with a newer compiler and when I managed to get OS X compiler tools working on NeXT, I had a vision of porting a small modern embedded library like Newlib to NEXTSTEP. I even (mostly) figured out how to create shared libraries and proposed a potential strategy how to proceed with such a system wide "upgrade".
This was largely captured in here:
http://www.nextcomputers.org/forums/index.php?msg=19959 (
http://www.nextcomputers.org/forums/index.php?msg=19959)