Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -40,10 +40,11 @@ OFURL.m \ OFXMLAttribute.m \ OFXMLElement.m \ OFXMLElementBuilder.m \ OFXMLParser.m \ + of_asprintf.m \ unicode.m INCLUDES := ${SRCS:.m=.h} \ OFCollection.h \ ObjFW.h \ Index: src/OFMutableString.m ================================================================== --- src/OFMutableString.m +++ src/OFMutableString.m @@ -31,11 +31,11 @@ #import "OFString.h" #import "OFExceptions.h" #import "macros.h" -#import "asprintf.h" +#import "of_asprintf.h" #import "unicode.h" @implementation OFMutableString - (void)_applyTable: (const of_unichar_t* const[])table withSize: (size_t)table_size @@ -230,25 +230,23 @@ - (void)appendFormat: (OFString*)fmt withArguments: (va_list)args { char *t; + int len; if (fmt == nil) @throw [OFInvalidArgumentException newWithClass: isa selector: _cmd]; - if ((vasprintf(&t, [fmt cString], args)) == -1) { - /* - * This is only the most likely error to happen. Unfortunately, - * there is no good way to check what really happened. - */ - @throw [OFOutOfMemoryException newWithClass: isa]; - } + if ((len = of_vasprintf(&t, [fmt cString], args)) == -1) + @throw [OFInvalidArgumentException newWithClass: isa + selector: _cmd]; @try { - [self appendCString: t]; + [self appendCString: t + withLength: len]; } @finally { free(t); } } Index: src/OFStream.m ================================================================== --- src/OFStream.m +++ src/OFStream.m @@ -33,11 +33,11 @@ #import "OFString.h" #import "OFDataArray.h" #import "OFExceptions.h" #import "macros.h" -#import "asprintf.h" +#import "of_asprintf.h" @implementation OFStream #ifndef _WIN32 + (void)initialize { @@ -674,24 +674,20 @@ } - (size_t)writeFormat: (OFString*)fmt withArguments: (va_list)args { - size_t len; char *t; + int len; if (fmt == nil) @throw [OFInvalidArgumentException newWithClass: isa selector: _cmd]; - if ((len = vasprintf(&t, [fmt cString], args)) == -1) { - /* - * This is only the most likely error to happen. Unfortunately, - * there is no good way to check what really happened. - */ - @throw [OFOutOfMemoryException newWithClass: isa]; - } + if ((len = of_vasprintf(&t, [fmt cString], args)) == -1) + @throw [OFInvalidArgumentException newWithClass: isa + selector: _cmd]; @try { return [self writeNBytes: len fromBuffer: t]; } @finally { Index: src/OFString.m ================================================================== --- src/OFString.m +++ src/OFString.m @@ -35,11 +35,11 @@ #import "OFFile.h" #import "OFAutoreleasePool.h" #import "OFExceptions.h" #import "macros.h" -#import "asprintf.h" +#import "of_asprintf.h" #import "unicode.h" extern const uint16_t of_iso_8859_15[256]; extern const uint16_t of_windows_1252[256]; @@ -464,26 +464,22 @@ arguments: (va_list)args { self = [super init]; @try { - int t; + int len; if (fmt == nil) @throw [OFInvalidArgumentException newWithClass: isa selector: _cmd]; - if ((t = vasprintf(&string, [fmt cString], args)) == -1) - /* - * This is only the most likely error to happen. - * Unfortunately, there is no good way to check what - * really happened. - */ - @throw [OFOutOfMemoryException newWithClass: isa]; + if ((len = of_vasprintf(&string, [fmt cString], args)) == -1) + @throw [OFInvalidArgumentException newWithClass: isa + selector: _cmd]; @try { - length = t; + length = len; switch (of_string_check_utf8(string, length)) { case 1: isUTF8 = YES; break; Index: src/OFXMLElement.m ================================================================== --- src/OFXMLElement.m +++ src/OFXMLElement.m @@ -113,11 +113,11 @@ ns = [ns_ copy]; if (stringval != nil) { OFAutoreleasePool *pool; - pool = [[OFAutoreleasePool alloc] init];; + pool = [[OFAutoreleasePool alloc] init]; [self addChild: [OFXMLElement elementWithCharacters: stringval]]; [pool release]; } Index: src/ObjFW.h ================================================================== --- src/ObjFW.h +++ src/ObjFW.h @@ -68,5 +68,6 @@ # import "OFThread.h" # import "threading.h" #endif #import "asprintf.h" +#import "of_asprintf.h" ADDED src/of_asprintf.h Index: src/of_asprintf.h ================================================================== --- src/of_asprintf.h +++ src/of_asprintf.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include + +#import "macros.h" + +extern int of_asprintf(char**, const char*, ...); +extern int of_vasprintf(char**, const char*, va_list); ADDED src/of_asprintf.m Index: src/of_asprintf.m ================================================================== --- src/of_asprintf.m +++ src/of_asprintf.m @@ -0,0 +1,521 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#import "OFString.h" +#import "OFAutoreleasePool.h" +#import "asprintf.h" + +#define MAX_SUBFMT_LEN 64 + +struct context { + const char *fmt; + size_t fmt_len; + char subfmt[MAX_SUBFMT_LEN + 1]; + size_t subfmt_len; + va_list args; + char *buf; + size_t buf_len; + size_t i, last; + enum { + STATE_STRING, + STATE_FORMAT_FLAGS, + STATE_FORMAT_FIELD_WIDTH, + STATE_FORMAT_LENGTH_MODIFIER, + STATE_FORMAT_CONVERSION_SPECIFIER + } state; + enum { + LENGTH_MODIFIER_NONE, + LENGTH_MODIFIER_HH, + LENGTH_MODIFIER_H, + LENGTH_MODIFIER_L, + LENGTH_MODIFIER_LL, + LENGTH_MODIFIER_J, + LENGTH_MODIFIER_Z, + LENGTH_MODIFIER_T, + LENGTH_MODIFIER_CAPITAL_L + } len_mod; +}; + +static bool +append_str(struct context *ctx, const char *astr, size_t astr_len) +{ + char *nbuf; + + if (astr_len == 0) + return true; + + if (ctx->buf == NULL) { + if ((ctx->buf = malloc(astr_len + 1)) == NULL) + return false; + + memcpy(ctx->buf, astr, astr_len); + ctx->buf_len = astr_len; + + return true; + } + + if ((nbuf = realloc(ctx->buf, ctx->buf_len + astr_len + 1)) == NULL) + return false; + + memcpy(nbuf + ctx->buf_len, astr, astr_len); + + ctx->buf = nbuf; + ctx->buf_len += astr_len; + + return true; +} + +static bool +append_subfmt(struct context *ctx, const char *asubfmt, size_t asubfmt_len) +{ + if (ctx->subfmt_len + asubfmt_len > MAX_SUBFMT_LEN) + return false; + + memcpy(ctx->subfmt + ctx->subfmt_len, asubfmt, asubfmt_len); + ctx->subfmt_len += asubfmt_len; + ctx->subfmt[ctx->subfmt_len] = 0; + + return true; +} + +static bool +state_string(struct context *ctx) +{ + if (ctx->fmt[ctx->i] == '%') { + if (ctx->i > 0) + if (!append_str(ctx, ctx->fmt + ctx->last, + ctx->i - ctx->last)) + return false; + + if (!append_subfmt(ctx, ctx->fmt + ctx->i, 1)) + return false; + + ctx->last = ctx->i + 1; + ctx->state = STATE_FORMAT_FLAGS; + } + + return true; +} + +static bool +state_format_flags(struct context *ctx) +{ + switch (ctx->fmt[ctx->i]) { + case '-': + case '+': + case ' ': + case '#': + case '0': + if (!append_subfmt(ctx, ctx->fmt + ctx->i, 1)) + return false; + + break; + default: + ctx->state = STATE_FORMAT_FIELD_WIDTH; + ctx->i--; + + break; + } + + return true; +} + +static bool +state_format_field_width(struct context *ctx) +{ + if ((ctx->fmt[ctx->i] >= '0' && ctx->fmt[ctx->i] <= '9') || + ctx->fmt[ctx->i] == '*' || ctx->fmt[ctx->i] == '.') { + if (!append_subfmt(ctx, ctx->fmt + ctx->i, 1)) + return false; + } else { + ctx->state = STATE_FORMAT_LENGTH_MODIFIER; + ctx->i--; + } + + return true; +} + +static bool +state_format_length_modifier(struct context *ctx) +{ + /* Only one allowed */ + switch (ctx->fmt[ctx->i]) { + case 'h': /* and also hh */ + if (ctx->fmt_len > ctx->i + 1 && ctx->fmt[ctx->i + 1] == 'h') { + if (!append_subfmt(ctx, ctx->fmt + ctx->i, 2)) + return false; + + ctx->i++; + ctx->len_mod = LENGTH_MODIFIER_HH; + } else { + if (!append_subfmt(ctx, ctx->fmt + ctx->i, 1)) + return false; + + ctx->len_mod = LENGTH_MODIFIER_H; + } + + break; + case 'l': /* and also ll */ + if (ctx->fmt_len > ctx->i + 1 && ctx->fmt[ctx->i + 1] == 'l') { + if (!append_subfmt(ctx, ctx->fmt + ctx->i, 2)) + return false; + + ctx->i++; + ctx->len_mod = LENGTH_MODIFIER_LL; + } else { + if (!append_subfmt(ctx, ctx->fmt + ctx->i, 1)) + return false; + + ctx->len_mod = LENGTH_MODIFIER_L; + } + + break; + case 'j': + if (!append_subfmt(ctx, ctx->fmt + ctx->i, 1)) + return false; + + ctx->len_mod = LENGTH_MODIFIER_J; + + break; + case 'z': + if (!append_subfmt(ctx, ctx->fmt + ctx->i, 1)) + return false; + + ctx->len_mod = LENGTH_MODIFIER_Z; + + break; + case 't': + if (!append_subfmt(ctx, ctx->fmt + ctx->i, 1)) + return false; + + ctx->len_mod = LENGTH_MODIFIER_T; + + break; + case 'L': + if (!append_subfmt(ctx, ctx->fmt + ctx->i, 1)) + return false; + + ctx->len_mod = LENGTH_MODIFIER_CAPITAL_L; + + break; + default: + ctx->i--; + + break; + } + + ctx->state = STATE_FORMAT_CONVERSION_SPECIFIER; + return true; +} + +static bool +state_format_conversion_specifier(struct context *ctx) +{ + char *tmp = NULL; + int tmp_len = 0; + + if (!append_subfmt(ctx, ctx->fmt + ctx->i, 1)) + return false; + + switch (ctx->fmt[ctx->i]) { + case '@':; + OFAutoreleasePool *pool; + + ctx->subfmt[ctx->subfmt_len - 1] = 's'; + + @try { + pool = [[OFAutoreleasePool alloc] init]; + } @catch (id e) { + [e release]; + return false; + } + + @try { + const char *desc = + [[va_arg(ctx->args, id) description] cString]; + + tmp_len = asprintf(&tmp, ctx->subfmt, desc); + } @catch (id e) { + [e release]; + return false; + } @finally { + [pool release]; + } + + break; + case 'd': + case 'i': + switch (ctx->len_mod) { + case LENGTH_MODIFIER_NONE: + case LENGTH_MODIFIER_HH: + case LENGTH_MODIFIER_H: + tmp_len = asprintf(&tmp, ctx->subfmt, + va_arg(ctx->args, int)); + break; + case LENGTH_MODIFIER_L: + tmp_len = asprintf(&tmp, ctx->subfmt, + va_arg(ctx->args, long)); + break; + case LENGTH_MODIFIER_LL: + tmp_len = asprintf(&tmp, ctx->subfmt, + va_arg(ctx->args, long long)); + break; + case LENGTH_MODIFIER_J: + tmp_len = asprintf(&tmp, ctx->subfmt, + va_arg(ctx->args, intmax_t)); + break; + case LENGTH_MODIFIER_Z: + tmp_len = asprintf(&tmp, ctx->subfmt, + va_arg(ctx->args, ssize_t)); + break; + case LENGTH_MODIFIER_T: + tmp_len = asprintf(&tmp, ctx->subfmt, + va_arg(ctx->args, ptrdiff_t)); + break; + default: + return false; + } + + break; + case 'o': case 'u': case 'x': case 'X': + switch (ctx->len_mod) { + case LENGTH_MODIFIER_NONE: + case LENGTH_MODIFIER_HH: + case LENGTH_MODIFIER_H: + tmp_len = asprintf(&tmp, ctx->subfmt, + va_arg(ctx->args, unsigned int)); + break; + case LENGTH_MODIFIER_L: + tmp_len = asprintf(&tmp, ctx->subfmt, + va_arg(ctx->args, unsigned long)); + break; + case LENGTH_MODIFIER_LL: + tmp_len = asprintf(&tmp, ctx->subfmt, + va_arg(ctx->args, unsigned long long)); + break; + case LENGTH_MODIFIER_J: + tmp_len = asprintf(&tmp, ctx->subfmt, + va_arg(ctx->args, uintmax_t)); + break; + case LENGTH_MODIFIER_Z: + tmp_len = asprintf(&tmp, ctx->subfmt, + va_arg(ctx->args, size_t)); + break; + case LENGTH_MODIFIER_T: + tmp_len = asprintf(&tmp, ctx->subfmt, + va_arg(ctx->args, ptrdiff_t)); + break; + default: + return false; + } + + break; + case 'f': + case 'F': + case 'e': + case 'E': + case 'g': + case 'G': + case 'a': + case 'A': + switch (ctx->len_mod) { + case LENGTH_MODIFIER_NONE: + case LENGTH_MODIFIER_L: + tmp_len = asprintf(&tmp, ctx->subfmt, + va_arg(ctx->args, double)); + break; + case LENGTH_MODIFIER_CAPITAL_L: + tmp_len = asprintf(&tmp, ctx->subfmt, + va_arg(ctx->args, long double)); + break; + default: + return false; + } + + break; + case 'c': + switch (ctx->len_mod) { + case LENGTH_MODIFIER_NONE: + tmp_len = asprintf(&tmp, ctx->subfmt, + va_arg(ctx->args, int)); + break; + case LENGTH_MODIFIER_L: + tmp_len = asprintf(&tmp, ctx->subfmt, + va_arg(ctx->args, wint_t)); + break; + default: + return false; + } + + break; + case 's': + switch (ctx->len_mod) { + case LENGTH_MODIFIER_NONE: + tmp_len = asprintf(&tmp, ctx->subfmt, + va_arg(ctx->args, const char*)); + break; + case LENGTH_MODIFIER_L: + tmp_len = asprintf(&tmp, ctx->subfmt, + va_arg(ctx->args, const wchar_t*)); + break; + default: + return false; + } + + break; + case 'p': + if (ctx->len_mod != LENGTH_MODIFIER_NONE) + return false; + + tmp_len = asprintf(&tmp, ctx->subfmt, va_arg(ctx->args, void*)); + + break; + case 'n': + switch (ctx->len_mod) { + case LENGTH_MODIFIER_NONE: + *va_arg(ctx->args, int*) = ctx->buf_len; + break; + case LENGTH_MODIFIER_HH: + *va_arg(ctx->args, signed char*) = ctx->buf_len; + break; + case LENGTH_MODIFIER_H: + *va_arg(ctx->args, short*) = ctx->buf_len; + break; + case LENGTH_MODIFIER_L: + *va_arg(ctx->args, long*) = ctx->buf_len; + break; + case LENGTH_MODIFIER_LL: + *va_arg(ctx->args, long long*) = ctx->buf_len; + break; + case LENGTH_MODIFIER_J: + *va_arg(ctx->args, intmax_t*) = ctx->buf_len; + break; + case LENGTH_MODIFIER_Z: + *va_arg(ctx->args, size_t*) = ctx->buf_len; + break; + case LENGTH_MODIFIER_T: + *va_arg(ctx->args, ptrdiff_t*) = ctx->buf_len; + break; + default: + return false; + } + + break; + case '%': + if (ctx->len_mod != LENGTH_MODIFIER_NONE) + return false; + + if (!append_str(ctx, "%", 1)) + return false; + + break; + default: + return false; + } + + if (tmp_len == -1) + return false; + + if (tmp != NULL) { + if (!append_str(ctx, tmp, tmp_len)) { + free(tmp); + return false; + } + + free(tmp); + } + + memset(ctx->subfmt, 0, MAX_SUBFMT_LEN); + ctx->subfmt_len = 0; + ctx->len_mod = LENGTH_MODIFIER_NONE; + + ctx->last = ctx->i + 1; + ctx->state = STATE_STRING; + + return true; +} + +static bool (*states[])(struct context*) = { + state_string, + state_format_flags, + state_format_field_width, + state_format_length_modifier, + state_format_conversion_specifier +}; + +int +of_vasprintf(char **ret, const char *fmt, va_list args) +{ + struct context ctx; + + ctx.fmt = fmt; + ctx.fmt_len = strlen(fmt); + memset(ctx.subfmt, 0, MAX_SUBFMT_LEN + 1); + ctx.subfmt_len = 0; + va_copy(ctx.args, args); + ctx.buf = NULL; + ctx.buf_len = 0; + ctx.last = 0; + ctx.state = STATE_STRING; + ctx.len_mod = LENGTH_MODIFIER_NONE; + + for (ctx.i = 0; ctx.i < ctx.fmt_len; ctx.i++) { + if (!states[ctx.state](&ctx)) { + if (ctx.buf != NULL) + free(ctx.buf); + + return -1; + } + } + + if (ctx.state != STATE_STRING) { + if (ctx.buf != NULL) + free(ctx.buf); + + return -1; + } + + if (!append_str(&ctx, ctx.fmt + ctx.last, ctx.fmt_len - ctx.last)) { + free(ctx.buf); + return -1; + } + + ctx.buf[ctx.buf_len] = 0; + + *ret = ctx.buf; + return ctx.buf_len; +} + +int +of_asprintf(char **ret, const char *fmt, ...) +{ + va_list args; + int r; + + va_start(args, fmt); + r = of_vasprintf(ret, fmt, args); + va_end(args); + + return r; +}