diff --git a/sc2/src/sc2code/libs/uio/uiostream.c b/sc2/src/sc2code/libs/uio/uiostream.c index f1b9852c5..eb101a50c 100644 --- a/sc2/src/sc2code/libs/uio/uiostream.c +++ b/sc2/src/sc2code/libs/uio/uiostream.c @@ -24,8 +24,10 @@ #include #include +#include #include "uioutils.h" +#include "utils.h" #ifdef uio_MEM_DEBUG # include "memdebug.h" #endif @@ -267,6 +269,46 @@ uio_ungetc(int c, uio_Stream *stream) { // return c; } +// NB. POSIX allows errno to be set for vsprintf(), but does not require it: +// "The value of errno may be set to nonzero by a library function call +// whether or not there is an error, provided the use of errno is not +// documented in the description of the function in this International +// Standard." The latter is the case for vsprintf(). +int +uio_vfprintf(uio_Stream *stream, const char *format, va_list args) { + // This could be done faster, but going through snprintf() is easiest, + // and is fast enough for now. + char *buf; + int putResult; + int savedErrno; + + buf = uio_vasprintf(format, args); + if (buf == NULL) { + // errno may or may not be set + return -1; + } + + putResult = uio_fputs(buf, stream); + savedErrno = errno; + + uio_free(buf); + + errno = savedErrno; + return putResult; +} + +int +uio_fprintf(uio_Stream *stream, const char *format, ...) { + va_list args; + int result; + + va_start(args, format); + result = uio_vfprintf(stream, format, args); + va_end(args); + + return result; +} + int uio_fputc(int c, uio_Stream *stream) { assert((stream->openFlags & O_ACCMODE) != O_RDONLY); diff --git a/sc2/src/sc2code/libs/uio/uiostream.h b/sc2/src/sc2code/libs/uio/uiostream.h index dedd95d36..da374969e 100644 --- a/sc2/src/sc2code/libs/uio/uiostream.h +++ b/sc2/src/sc2code/libs/uio/uiostream.h @@ -26,6 +26,8 @@ typedef struct uio_Stream uio_Stream; #include "io.h" +#include + uio_Stream *uio_fopen(uio_DirHandle *dir, const char *path, const char *mode); int uio_fclose(uio_Stream *stream); @@ -34,6 +36,8 @@ char *uio_fgets(char *buf, int size, uio_Stream *stream); int uio_fgetc(uio_Stream *stream); #define uio_getc uio_fgetc int uio_ungetc(int c, uio_Stream *stream); +int uio_vfprintf(uio_Stream *stream, const char *format, va_list args); +int uio_fprintf(uio_Stream *stream, const char *format, ...); int uio_fputc(int c, uio_Stream *stream); #define uio_putc uio_fputc int uio_fputs(const char *s, uio_Stream *stream); diff --git a/sc2/src/sc2code/libs/uio/utils.c b/sc2/src/sc2code/libs/uio/utils.c index fcd01e93e..b7be7983f 100644 --- a/sc2/src/sc2code/libs/uio/utils.c +++ b/sc2/src/sc2code/libs/uio/utils.c @@ -364,3 +364,98 @@ uio_StdioAccessHandle_free(uio_StdioAccessHandle *handle) { uio_free(handle); } +// The result should be freed using uio_free(). +// NB. POSIX allows errno to be set for vsprintf(), but does not require it: +// "The value of errno may be set to nonzero by a library function call +// whether or not there is an error, provided the use of errno is not +// documented in the description of the function in this International +// Standard." The latter is the case for vsprintf(). +char * +uio_vasprintf(const char *format, va_list args) { + // TODO: If there is a system vasprintf, use that. + // XXX: That would mean that the allocation would always go through + // malloc() or so, instead of uio_malloc(), which may not be + // desirable. + + char *buf; + size_t bufSize = 128; + // Start with enough for one screen line, and a power of 2, + // which might give faster result with allocations. + + buf = uio_malloc(bufSize); + if (buf == NULL) { + // errno is set. + return NULL; + } + + for (;;) { + int printResult = vsnprintf(buf, bufSize, format, args); + if (printResult < 0) { + // This means the buffer was not small enough, but vsnprintf() + // does not give us any clue on how large it should be. + // Note that this does not happen with a C'99 compliant + // vsnprintf(), but it will happen on MS Windows, and on + // glibc before version 2.1. + bufSize *= 2; + } else if ((unsigned int) printResult >= bufSize) { + // The buffer was too small, but printResult contains the size + // that the buffer needs to be (excluding the '\0' character). + bufSize = printResult + 1; + } else { + // Success. + if ((unsigned int) printResult + 1 != bufSize) { + // Shorten the resulting buffer to the size that was + // actually needed. + char *newBuf = uio_realloc(buf, printResult + 1); + if (newBuf == NULL) { + // We could have returned the (overly large) original + // buffer, but the unused memory might not be + // acceptable, and the program would be likely to run + // into problems sooner or later anyhow. + int savedErrno = errno; + uio_free(buf); + errno = savedErrno; + return NULL; + } + return newBuf; + } + + return buf; + } + + { + char *newBuf = uio_realloc(buf, bufSize); + if (newBuf == NULL) + { + int savedErrno = errno; + uio_free(buf); + errno = savedErrno; + return NULL; + } + buf = newBuf; + } + } +} + +// As uio_vasprintf(), but with an argument list. +char * +uio_asprintf(const char *format, ...) { + // TODO: If there is a system asprintf, use that. + // XXX: That would mean that the allocation would always go through + // malloc() or so, instead of uio_malloc(), which may not be + // desirable. + + va_list args; + char *result; + int savedErrno; + + va_start(args, format); + result = uio_vasprintf(format, args); + savedErrno = errno; + va_end(args); + + errno = savedErrno; + return result; +} + + diff --git a/sc2/src/sc2code/libs/uio/utils.h b/sc2/src/sc2code/libs/uio/utils.h index 5813bce33..11cf5ce29 100644 --- a/sc2/src/sc2code/libs/uio/utils.h +++ b/sc2/src/sc2code/libs/uio/utils.h @@ -21,6 +21,8 @@ #ifndef _UTILS_H #define _UTILS_H +#include + #ifdef uio_INTERNAL typedef struct uio_StdioAccessHandle uio_StdioAccessHandle; #else @@ -34,6 +36,8 @@ uio_StdioAccessHandle *uio_getStdioAccess(uio_DirHandle *dir, const char *uio_StdioAccessHandle_getPath(uio_StdioAccessHandle *handle); void uio_releaseStdioAccess(uio_StdioAccessHandle *handle); +char *uio_vasprintf(const char *format, va_list args); +char *uio_asprintf(const char *format, ...); #endif /* _UTILS_H */