vasprintf, vsnprintf, asprintf, and snprintf

Started by verdraith, January 02, 2023, 11:05:46 AM

Previous topic - Next topic

verdraith

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;
}

Lisp Hacker

t-rexky

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