Index: src/OFExceptions.h ================================================================== --- src/OFExceptions.h +++ src/OFExceptions.h @@ -135,10 +135,20 @@ /** * An OFException indicating that the encoding is invalid for this object. */ @interface OFInvalidEncodingException: OFException {} +/** + * \return An error message for the exception as a C string + */ +- (const char*)cString; +@end + +/** + * An OFException indicating that the format is invalid. + */ +@interface OFInvalidFormatException: OFException {} /** * \return An error message for the exception as a C string */ - (const char*)cString; @end Index: src/OFExceptions.m ================================================================== --- src/OFExceptions.m +++ src/OFExceptions.m @@ -161,10 +161,23 @@ return string; asprintf(&string, "The encoding is invalid for object of classs %s!", [object name]); + return string; +} +@end + +@implementation OFInvalidFormatException +- (const char*)cString +{ + if (string != NULL) + return string; + + asprintf(&string, "The format is invalid for object of classs %s!", + [object name]); + return string; } @end @implementation OFInitializationFailedException Index: src/OFObject.h ================================================================== --- src/OFObject.h +++ src/OFObject.h @@ -28,10 +28,19 @@ * * \return An initialized object */ - init; +/** + * Adds a pointer to the memory pool. + * This is useful to add memory allocated by functions such as asprintf to the + * pool so it gets freed automatically when the object is freed. + * + * \param ptr A pointer to add to the memory pool + */ +- addToMemoryPool: (void*)ptr; + /** * Allocate memory and store it in the objects memory pool so it can be free'd * automatically when the object is free'd. * * \param size The size of the memory to allocate Index: src/OFObject.m ================================================================== --- src/OFObject.m +++ src/OFObject.m @@ -41,10 +41,33 @@ free(self); return nil; } + +- addToMemoryPool: (void*)ptr +{ + void **memchunks; + size_t memchunks_size; + + memchunks_size = __memchunks_size + 1; + + if (SIZE_MAX - __memchunks_size == 0 || + memchunks_size > SIZE_MAX / sizeof(void*)) + @throw [OFOutOfRangeException newWithObject: self]; + + if ((memchunks = realloc(__memchunks, + memchunks_size * sizeof(void*))) == NULL) + @throw [OFNoMemException newWithObject: self + andSize: memchunks_size]; + + __memchunks = memchunks; + __memchunks[__memchunks_size] = ptr; + __memchunks_size = memchunks_size; + + return ptr; +} - (void*)getMemWithSize: (size_t)size { void *ptr, **memchunks; size_t memchunks_size; Index: src/OFString.h ================================================================== --- src/OFString.h +++ src/OFString.h @@ -6,10 +6,12 @@ * * This file is part of libobjfw. It may be distributed under the terms of the * Q Public License 1.0, which can be found in the file LICENSE included in * the packaging of this file. */ + +#import #import "OFObject.h" /** * A class for storing and modifying strings. @@ -34,10 +36,30 @@ * \param str A C string to initialize the OFString with * \return A new OFString */ + newFromCString: (const char*)str; +/** + * Creates a new OFString from a format C string. + * See printf for the format syntax. + * + * \param fmt A C string used as format to initialize the OFString + * \return A new OFString + */ ++ newFromFormatCString: (const char*)fmt, ...; + +/** + * Creates a new OFString from a format C string. + * See printf for the format syntax. + * + * \param fmt A C string used as format to initialize the OFString + * \param args The arguments used in the format string + * \return A new OFString + */ ++ newFromFormatCString: (const char*)fmt + withArguments: (va_list)args; + /** * Initializes an already allocated OFString. * * \return An initialized OFString */ @@ -49,10 +71,30 @@ * \param str A C string to initialize the OFString with * \return An initialized OFString */ - initFromCString: (const char*)str; +/** + * Initializes an already allocated OFString from a format C string. + * See printf for the format syntax. + * + * \param fmt A C string used as format to initialize the OFString + * \return An initialized OFString + */ +- initFromFormatCString: (const char*)fmt, ...; + +/** + * Initializes an already allocated OFString from a format C string. + * See printf for the format syntax. + * + * \param fmt A C string used as format to initialize the OFString + * \param args The arguments used in the format string + * \return An initialized OFString + */ +- initFromFormatCString: (const char*)fmt + withArguments: (va_list)args; + /** * \return The OFString as a wide C string */ - (const char*)cString; @@ -96,10 +138,28 @@ * * \param str A C string to append */ - appendCString: (const char*)str; +/** + * Append a formatted C string to the OFString. + * See printf for the format syntax. + * + * \param fmt A format C string which generates the string to append + */ +- appendWithFormatCString: (const char*)fmt, ...; + +/** + * Append a formatted C string to the OFString. + * See printf for the format syntax. + * + * \param fmt A format C string which generates the string to append + * \param args The arguments used in the format string + */ +- appendWithFormatCString: (const char*)fmt + andArguments: (va_list)args; + /** * Reverse the OFString. */ - reverse; Index: src/OFString.m ================================================================== --- src/OFString.m +++ src/OFString.m @@ -9,10 +9,11 @@ * the packaging of this file. */ #import "config.h" +#import #import #import #import #ifdef HAVE_SYS_MMAN_H @@ -103,10 +104,30 @@ + newFromCString: (const char*)str { return [[self alloc] initFromCString: str]; } + ++ newFromFormatCString: (const char*)fmt, ... +{ + id ret; + va_list args; + + va_start(args, fmt); + ret = [[self alloc] initFromFormatCString: fmt + withArguments: args]; + va_end(args); + + return ret; +} + ++ newFromFormatCString: (const char*)fmt + withArguments: (va_list)args +{ + return [[self alloc] initFromFormatCString: fmt + withArguments: args]; +} - init { if ((self = [super init])) { length = 0; @@ -135,10 +156,63 @@ string = [self getMemWithSize: length + 1]; memcpy(string, str, length + 1); } } + + return self; +} + +- initFromFormatCString: (const char*)fmt, ... +{ + id ret; + va_list args; + + va_start(args, fmt); + ret = [self initFromFormatCString: fmt + withArguments: args]; + va_end(args); + + return ret; +} + +- initFromFormatCString: (const char*)fmt + withArguments: (va_list)args +{ + int t; + + if ((self = [super init])) { + if (fmt == NULL) + @throw [OFInvalidFormatException newWithObject: self]; + + if ((t = vasprintf(&string, fmt, args)) == -1) + /* + * This is only the most likely error to happen. + * Unfortunately, as errno isn't always thread-safe, + * there's no good way for us to find out what really + * happened. + */ + @throw [OFNoMemException newWithObject: self]; + length = t; + + switch (check_utf8(string, length)) { + case 1: + is_utf8 = YES; + break; + case -1: + free(string); + [super free]; + @throw [OFInvalidEncodingException newWithObject: self]; + } + + @try { + [self addToMemoryPool: string]; + } @catch (OFException *e) { + free(string); + @throw e; + } + } return self; } - (const char*)cString @@ -196,10 +270,45 @@ memcpy(newstr + length, str, strlength + 1); length = newlen; string = newstr; + + return self; +} + +- appendWithFormatCString: (const char*)fmt, ... +{ + id ret; + va_list args; + + va_start(args, fmt); + ret = [self appendWithFormatCString: fmt + andArguments: args]; + va_end(args); + + return ret; +} + +- appendWithFormatCString: (const char*)fmt + andArguments: (va_list)args +{ + char *t; + + if (fmt == NULL) + @throw [OFInvalidFormatException newWithObject: self]; + + if ((vasprintf(&t, fmt, args)) == -1) + /* + * This is only the most likely error to happen. + * Unfortunately, as errno isn't always thread-safe, there's + * no good way for us to find out what really happened. + */ + @throw [OFNoMemException newWithObject: self]; + + [self appendCString: t]; + free(t); return self; } - reverse Index: src/asprintf.c ================================================================== --- src/asprintf.c +++ src/asprintf.c @@ -14,22 +14,29 @@ #include #include #include int -asprintf(char **strp, const char *fmt, ...) +vasprintf(char **strp, const char *fmt, va_list args) { - int ret, size; - va_list args; - - va_start(args, fmt); + int size; if ((size = vsnprintf(NULL, 0, fmt, args)) < 0) return size; if ((*strp = malloc((size_t)size + 1)) == NULL) return -1; - ret = vsnprintf(*strp, (size_t)size + 1, fmt, args); + return vsnprintf(*strp, (size_t)size + 1, fmt, args); +} + +int +asprintf(char **strp, const char *fmt, ...) +{ + int ret; + va_list args; + + va_start(args, fmt); + ret = vasprintf(strp, fmt, args); va_end(args); return ret; } Index: src/asprintf.h ================================================================== --- src/asprintf.h +++ src/asprintf.h @@ -8,5 +8,6 @@ * Q Public License 1.0, which can be found in the file LICENSE included in * the packaging of this file. */ extern int asprintf(char**, const char*, ...); +extern int vasprintf(char**, const char*, va_list); Index: tests/OFString/OFString.m ================================================================== --- tests/OFString/OFString.m +++ tests/OFString/OFString.m @@ -21,11 +21,11 @@ #define ZD "%zd" #else #define ZD "%u" #endif -#define NUM_TESTS 10 +#define NUM_TESTS 12 #define SUCCESS \ printf("\r\033[1;%dmTests successful: " ZD "/%d\033[0m", \ (i == NUM_TESTS - 1 ? 32 : 33), i + 1, NUM_TESTS); \ fflush(stdout); #define FAIL \ @@ -84,10 +84,18 @@ CHECK_EXCEPT(s1 = [OFString newFromCString: "\xF0\x80\x80\xC0"], OFInvalidEncodingException) s1 = [OFString newFromCString: "äöü€𝄞"]; CHECK(!strcmp([[s1 reverse] cString], "𝄞€üöä")) + [s1 free]; + + /* Format tests */ + s1 = [OFString newFromFormatCString: "%s: %d", "test", 123]; + CHECK(!strcmp([s1 cString], "test: 123")) + + [s1 appendWithFormatCString: "%02X", 15]; + CHECK(!strcmp([s1 cString], "test: 1230F")) puts(""); return 0; }