Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -9,25 +9,17 @@ OFFile.m \ OFList.m \ OFListObject.m \ OFObject.m \ OFString.m \ - OFWideCString.m - -INCLUDES = OFConstString.h \ - OFCString.h \ - OFConstWideString.h \ - OFExceptions.h \ - OFFile.h \ - OFList.h \ - OFListObject.h \ - OFObject.h \ - OFString.h \ - OFWideString.h + OFWideCString.m \ + OFXMLFactory.m + +INCLUDES = ${SRCS:.m=.h} include ../buildsys.mk CPPFLAGS += -I.. OBJCFLAGS += ${LIB_CFLAGS} LD = ${OBJC} LDFLAGS += ${LIB_LDFLAGS} LIBS += -lobjc ADDED src/OFXMLFactory.h Index: src/OFXMLFactory.h ================================================================== --- src/OFXMLFactory.h +++ src/OFXMLFactory.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2008 + * Jonathan Schleifer + * + * All rights reserved. + * + * 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 "OFObject.h" + +@interface OFXMLFactory: OFObject ++ (char*)escapeCString: (const char*)s; ++ (char*)createStanza: (const char*)name + withCloseTag: (BOOL)close + andCData: (const char*)cdata, ...; ++ (char*)concatAndFreeCStrings: (char **)strs; +@end ADDED src/OFXMLFactory.m Index: src/OFXMLFactory.m ================================================================== --- src/OFXMLFactory.m +++ src/OFXMLFactory.m @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2008 + * Jonathan Schleifer + * + * All rights reserved. + * + * 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 +#import +#import + +#import "OFXMLFactory.h" +#import "OFExceptions.h" + +/* + * We don't use OFString in this file for performance reasons! + * + * We already have a clue about how big the resulting string will get, so we + * can prealloc and only resize when really necessary - OFString would always + * resize when we append, which would be slow here. + */ + +inline BOOL +xmlfactory_resize(char **str, size_t *len, size_t add) +{ + char *str2; + size_t len2; + + len2 = *len + add; + + if ((str2 = realloc(*str, len2)) == NULL) { + if (*str) + free(*str); + *str = NULL; + return NO; + } + + *str = str2; + *len = len2; + + return YES; +} + +inline BOOL +xmlfactory_add2str(char **str, size_t *len, size_t *pos, const char *add) +{ + size_t add_len; + + add_len = strlen(add); + + if (!xmlfactory_resize(str, len, add_len)) + return NO; + + memcpy(*str + *pos, add, add_len); + *pos += add_len; + + return YES; +} + +@implementation OFXMLFactory ++ (char*)escapeCString: (const char*)s +{ + char *ret; + size_t i, j, len, nlen; + + len = nlen = strlen(s); + + if ((ret = malloc(len + 1)) == NULL) { + [[OFNoMemException newWithObject: nil + andSize: len + 1] raise]; + return NULL; + } + + for (i = j = 0; i < len; i++) { + switch (s[i]) { + case '<': + if (!xmlfactory_add2str(&ret, &nlen, &j, "<")) { + [[OFNoMemException newWithObject: nil + andSize: nlen + 4] + raise]; + return NULL; + } + break; + case '>': + if (!xmlfactory_add2str(&ret, &nlen, &j, ">")) { + [[OFNoMemException newWithObject: nil + andSize: nlen + 4] + raise]; + return NULL; + } + break; + case '"': + if (!xmlfactory_add2str(&ret, &nlen, &j, """)) { + [[OFNoMemException newWithObject: nil + andSize: nlen + 6] + raise]; + return NULL; + } + break; + case '\'': + if (!xmlfactory_add2str(&ret, &nlen, &j, "'")) { + [[OFNoMemException newWithObject: nil + andSize: nlen + 6] + raise]; + return NULL; + } + break; + case '&': + if (!xmlfactory_add2str(&ret, &nlen, &j, "&")) { + [[OFNoMemException newWithObject: nil + andSize: nlen + 5] + raise]; + return NULL; + } + break; + default: + ret[j++] = s[i]; + break; + } + } + + ret[j] = 0; + return ret; +} + ++ (char*)createStanza: (const char*)name + withCloseTag: (BOOL)close + andCData: (const char*)cdata, ... +{ + char *arg, *val, *xml; + size_t i, len; + va_list args; + + /* Start of tag */ + len = strlen(name) + 3; + if ((xml = malloc(len)) == NULL) { + [[OFNoMemException newWithObject: nil + andSize: len] raise]; + return NULL; + } + + i = 0; + xml[i++] = '<'; + memcpy(xml + i, name, strlen(name)); + i += strlen(name); + + /* Arguments */ + va_start(args, cdata); + while ((arg = va_arg(args, char*)) != NULL && + (val = va_arg(args, char*)) != NULL) { + char *esc_val; + + if ((esc_val = [OFXMLFactory escapeCString: val]) == NULL) { + /* + * escapeCString already throws an exception, + * no need to throw a second one here. + */ + free(xml); + return NULL; + } + + if (!xmlfactory_resize(&xml, &len, 1 + strlen(arg) + 2 + + strlen(esc_val) + 1)) { + free(esc_val); + [[OFNoMemException newWithObject: nil + andSize: len + 1 + + strlen(arg) + 2 + + strlen(esc_val) + 1] + raise]; + return NULL; + } + + xml[i++] = ' '; + memcpy(xml + i, arg, strlen(arg)); + i += strlen(arg); + xml[i++] = '='; + xml[i++] = '\''; + memcpy(xml + i, esc_val, strlen(esc_val)); + i += strlen(esc_val); + xml[i++] = '\''; + + free(esc_val); + } + va_end(args); + + /* End of tag */ + if (close) { + if (cdata == NULL) { + if (!xmlfactory_resize(&xml, &len, 2 - 1)) { + [[OFNoMemException newWithObject: nil + andSize: len + 2 - 1] + raise]; + return NULL; + } + + xml[i++] = '/'; + xml[i++] = '>'; + } else { + if (!xmlfactory_resize(&xml, &len, 1 + strlen(cdata) + + 2 + strlen(name) + 1 - 1)) { + [[OFNoMemException newWithObject: nil + andSize: len + 1 + + strlen( + cdata) + + 2 + + strlen(name) + + 1 - 1] + raise]; + return NULL; + } + + xml[i++] = '>'; + memcpy(xml + i, cdata, strlen(cdata)); + i += strlen(cdata); + xml[i++] = '<'; + xml[i++] = '/'; + memcpy(xml + i, name, strlen(name)); + i += strlen(name); + xml[i++] = '>'; + } + } else + xml[i++] = '>'; + + xml[i] = 0; + return xml; +} + ++ (char*)concatAndFreeCStrings: (char **)strs +{ + char *ret; + size_t i, len, pos; + + if (strs[0] == NULL) + return NULL; + + len = strlen(*strs) + 1; + + if ((ret = malloc(len)) == NULL) { + [[OFNoMemException newWithObject: nil + andSize: len] raise]; + return NULL; + } + + memcpy(ret, strs[0], len - 1); + pos = len - 1; + + for (i = 1; strs[i] != NULL; i++) { + if (!xmlfactory_add2str(&ret, &len, &pos, strs[i])) { + free(ret); + [[OFNoMemException newWithObject: nil + andSize: len + strlen(strs[i])] + raise]; + return NULL; + } + } + + for (i = 0; strs[i] != NULL; i++) + free(strs[i]); + + ret[pos] = 0; + return ret; +} +@end Index: tests/Makefile ================================================================== --- tests/Makefile +++ tests/Makefile @@ -1,3 +1,3 @@ -SUBDIRS = OFObject OFString OFList OFWideString +SUBDIRS = OFObject OFString OFList OFWideString OFXMLFactory include ../buildsys.mk ADDED tests/OFXMLFactory/Makefile Index: tests/OFXMLFactory/Makefile ================================================================== --- tests/OFXMLFactory/Makefile +++ tests/OFXMLFactory/Makefile @@ -0,0 +1,19 @@ +PROG_NOINST = ofxmlfactory +SRCS = OFXMLFactory.m + +include ../../buildsys.mk + +CPPFLAGS += -I../../src +LIBS += -lobjc -L../../src -lobjfw + +.PHONY: run + +all: run +run: ${PROG_NOINST} + rm -f libobjfw.so.1 libobjfw.dylib + ln -s ../../src/libobjfw.so libobjfw.so.1 + ln -s ../../src/libobjfw.dylib libobjfw.dylib + LD_LIBRARY_PATH=. \ + DYLD_LIBRARY_PATH=. \ + ./${PROG_NOINST} + rm -f libobjfw.so.1 libobjfw.dylib ADDED tests/OFXMLFactory/OFXMLFactory.m Index: tests/OFXMLFactory/OFXMLFactory.m ================================================================== --- tests/OFXMLFactory/OFXMLFactory.m +++ tests/OFXMLFactory/OFXMLFactory.m @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2008 + * Jonathan Schleifer + * + * All rights reserved. + * + * 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 +#import + +#import + +#import "OFXMLFactory.h" + +/* TODO: Do not only print, but check if it's the output it should be */ + +inline int +test_concat() +{ + const char *c1 = "", *c2 = "bar", *c3 = ""; + char *s1, *s2, *s3, *str; + char *strs[4]; + + if ((s1 = malloc(strlen(c1) + 1)) == NULL || + (s2 = malloc(strlen(c2) + 1)) == NULL || + (s3 = malloc(strlen(c3) + 1)) == NULL) + return 1; + + strncpy(s1, c1, strlen(c1) + 1); + strncpy(s2, c2, strlen(c2) + 1); + strncpy(s3, c3, strlen(c3) + 1); + + strs[0] = s1; + strs[1] = s2; + strs[2] = s3; + strs[3] = NULL; + + puts((str = [OFXMLFactory concatAndFreeCStrings: strs])); + free(str); + + return 0; +} + +inline int +test_create_stanza() +{ + char *xml; + + xml = [OFXMLFactory createStanza: "foo" + withCloseTag: NO + andCData: NULL, + NULL]; + puts(xml); + free(xml); + + xml = [OFXMLFactory createStanza: "foo" + withCloseTag: NO + andCData: NULL, + "bar", "baz", + "blub", "asd", + NULL]; + puts(xml); + free(xml); + + xml = [OFXMLFactory createStanza: "foo" + withCloseTag: YES + andCData: NULL, + NULL]; + puts(xml); + free(xml); + + xml = [OFXMLFactory createStanza: "foo" + withCloseTag: YES + andCData: "bar", + NULL]; + puts(xml); + free(xml); + + xml = [OFXMLFactory createStanza: "foo" + withCloseTag: YES + andCData: NULL, + "bar", "b&az", + NULL]; + puts(xml); + free(xml); + + xml = [OFXMLFactory createStanza: "foo" + withCloseTag: YES + andCData: "bar", + "bar", "b'az", + NULL]; + puts(xml); + free(xml); + + xml = [OFXMLFactory createStanza: "foo" + withCloseTag: YES + andCData: NULL, + "bar", "b&az", + "x", "asd\"", + NULL]; + puts(xml); + free(xml); + + xml = [OFXMLFactory createStanza: "foo" + withCloseTag: YES + andCData: "bar", + "bar", "b'az", + "x", "y", + "a", "b", + NULL]; + puts(xml); + free(xml); + + return 0; +} + +inline int +test_escape() +{ + char *tmp; + + tmp = [OFXMLFactory escapeCString: " &welt'\"!&"]; + puts(tmp); + free(tmp); + + return 0; +} + +int main() +{ + assert(test_escape() == 0); + assert(test_create_stanza() == 0); + assert(test_concat() == 0); + + return 0; +}