/*
* Copyright (c) 2008
* Jonathan Schleifer <js@webkeks.org>
*
* 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 "config.h"
#import <stdarg.h>
#import <stdlib.h>
#import <string.h>
#import <wchar.h>
#import "OFXMLFactory.h"
#import "OFExceptions.h"
#import "OFMacros.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.
*/
static inline BOOL
xf_resize_chars(char **str, size_t *len, size_t add)
{
char *str2;
size_t len2;
if (add > SIZE_MAX - *len)
@throw [OFOutOfRangeException newWithObject: nil];
len2 = *len + add;
if ((str2 = realloc(*str, len2)) == NULL) {
if (*str)
free(*str);
*str = NULL;
return NO;
}
*str = str2;
*len = len2;
return YES;
}
static inline BOOL
xf_resize_wchars(wchar_t **str, size_t *len, size_t add)
{
wchar_t *str2;
size_t len2;
if (add > SIZE_MAX - *len)
@throw [OFOutOfRangeException newWithObject: nil];
len2 = *len + add;
if (len2 > SIZE_MAX / sizeof(wchar_t))
@throw [OFOutOfRangeException newWithObject: nil];
if ((str2 = realloc(*str, len2 * sizeof(wchar_t))) == NULL) {
if (*str)
free(*str);
*str = NULL;
return NO;
}
*str = str2;
*len = len2;
return YES;
}
static inline BOOL
xf_add2chars(char **str, size_t *len, size_t *pos, const char *add)
{
size_t add_len;
add_len = strlen(add);
if (!xf_resize_chars(str, len, add_len))
return NO;
memcpy(*str + *pos, add, add_len);
*pos += add_len;
return YES;
}
static inline BOOL
xf_add2wchars(wchar_t **str, size_t *len, size_t *pos, const wchar_t *add)
{
size_t add_len;
add_len = wcslen(add);
if (!xf_resize_wchars(str, len, add_len))
return NO;
wmemcpy(*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 (SIZE_MAX - len < 1)
@throw [OFOutOfRangeException newWithObject: nil];
nlen++;
if ((ret = malloc(nlen)) == NULL)
@throw [OFNoMemException newWithObject: nil
andSize: nlen];
for (i = j = 0; i < len; i++) {
switch (s[i]) {
case '<':
if (OF_UNLIKELY(!xf_add2chars(&ret, &nlen, &j, "<")))
@throw [OFNoMemException
newWithObject: nil
andSize: nlen + 4];
break;
case '>':
if (OF_UNLIKELY(!xf_add2chars(&ret, &nlen, &j, ">")))
@throw [OFNoMemException
newWithObject: nil
andSize: nlen + 4];
break;
case '"':
if (OF_UNLIKELY(!xf_add2chars(&ret, &nlen, &j,
""")))
@throw [OFNoMemException
newWithObject: nil
andSize: nlen + 6];
break;
case '\'':
if (OF_UNLIKELY(!xf_add2chars(&ret, &nlen, &j,
"'")))
@throw [OFNoMemException
newWithObject: nil
andSize: nlen + 6];
break;
case '&':
if (OF_UNLIKELY(!xf_add2chars(&ret, &nlen, &j,
"&")))
@throw [OFNoMemException
newWithObject: nil
andSize: nlen + 5];
break;
default:
ret[j++] = s[i];
break;
}
}
ret[j] = 0;
return ret;
}
+ (wchar_t*)escapeWideCString: (const wchar_t*)s
{
wchar_t *ret;
size_t i, j, len, nlen;
len = nlen = wcslen(s);
if (SIZE_MAX - len < 1)
@throw [OFOutOfRangeException newWithObject: nil];
nlen++;
if (nlen > SIZE_MAX / sizeof(wchar_t))
@throw [OFOutOfRangeException newWithObject: nil];
if ((ret = malloc(nlen * sizeof(wchar_t))) == NULL)
@throw [OFNoMemException newWithObject: nil
andSize: nlen * sizeof(wchar_t)];
for (i = j = 0; i < len; i++) {
switch (s[i]) {
case L'<':
if (OF_UNLIKELY(!xf_add2wchars(&ret, &nlen, &j,
L"<")))
@throw [OFNoMemException
newWithObject: nil
andSize: (nlen + 4) *
sizeof(wchar_t)];
break;
case L'>':
if (OF_UNLIKELY(!xf_add2wchars(&ret, &nlen, &j,
L">")))
@throw [OFNoMemException
newWithObject: nil
andSize: (nlen + 4) *
sizeof(wchar_t)];
break;
case L'"':
if (OF_UNLIKELY(!xf_add2wchars(&ret, &nlen, &j,
L""")))
@throw [OFNoMemException
newWithObject: nil
andSize: (nlen + 6) *
sizeof(wchar_t)];
break;
case L'\'':
if (OF_UNLIKELY(!xf_add2wchars(&ret, &nlen, &j,
L"'")))
@throw [OFNoMemException
newWithObject: nil
andSize: (nlen + 6) *
sizeof(wchar_t)];
break;
case L'&':
if (OF_UNLIKELY(!xf_add2wchars(&ret, &nlen, &j,
L"&")))
@throw [OFNoMemException
newWithObject: nil
andSize: (nlen + 5) *
sizeof(wchar_t)];
break;
default:
ret[j++] = s[i];
break;
}
}
ret[j] = 0;
return ret;
}
+ (char*)createStanza: (const char*)name
withCloseTag: (BOOL)close
andData: (const char*)data, ...
{
char *arg, *val, *xml;
size_t i, len;
va_list args;
/* Start of tag */
len = strlen(name);
if (SIZE_MAX - len < 3)
@throw [OFOutOfRangeException newWithObject: nil];
len += 3;
if ((xml = malloc(len)) == NULL)
@throw [OFNoMemException newWithObject: nil
andSize: len];
i = 0;
xml[i++] = '<';
memcpy(xml + i, name, strlen(name));
i += strlen(name);
/* Arguments */
va_start(args, data);
while ((arg = va_arg(args, char*)) != NULL &&
(val = va_arg(args, char*)) != NULL) {
char *esc_val;
if (OF_UNLIKELY((esc_val =
[OFXMLFactory escapeCString: val]) == NULL)) {
/*
* escapeCString already throws an exception,
* no need to throw a second one here.
*/
free(xml);
return NULL;
}
if (OF_UNLIKELY(!xf_resize_chars(&xml, &len, 1 + strlen(arg) +
2 + strlen(esc_val) + 1))) {
free(esc_val);
@throw [OFNoMemException
newWithObject: nil
andSize: len + 1 + strlen(arg) + 2 +
strlen(esc_val) + 1];
}
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 (data == NULL) {
if (!xf_resize_chars(&xml, &len, 2 - 1))
@throw [OFNoMemException
newWithObject: nil
andSize: len + 2 - 1];
xml[i++] = '/';
xml[i++] = '>';
} else {
if (!xf_resize_chars(&xml, &len, 1 + strlen(data) +
2 + strlen(name) + 1 - 1))
@throw [OFNoMemException
newWithObject: nil
andSize: len + 1 + strlen(data) + 2 +
strlen(name) + 1 - 1];
xml[i++] = '>';
memcpy(xml + i, data, strlen(data));
i += strlen(data);
xml[i++] = '<';
xml[i++] = '/';
memcpy(xml + i, name, strlen(name));
i += strlen(name);
xml[i++] = '>';
}
} else
xml[i++] = '>';
xml[i] = 0;
return xml;
}
+ (wchar_t*)createWideStanza: (const wchar_t*)name
withCloseTag: (BOOL)close
andData: (const wchar_t*)data, ...
{
wchar_t *arg, *val, *xml;
size_t i, len;
va_list args;
/* Start of tag */
len = wcslen(name);
if (SIZE_MAX - len < 3)
@throw [OFOutOfRangeException newWithObject: nil];
len += 3;
if (len > SIZE_MAX / sizeof(wchar_t))
@throw [OFOutOfRangeException newWithObject: nil];
if ((xml = malloc(len * sizeof(wchar_t))) == NULL)
@throw [OFNoMemException newWithObject: nil
andSize: len * sizeof(wchar_t)];
i = 0;
xml[i++] = L'<';
wmemcpy(xml + i, name, wcslen(name));
i += wcslen(name);
/* Arguments */
va_start(args, data);
while ((arg = va_arg(args, wchar_t*)) != NULL &&
(val = va_arg(args, wchar_t*)) != NULL) {
wchar_t *esc_val;
if (OF_UNLIKELY((esc_val =
[OFXMLFactory escapeWideCString: val]) == NULL)) {
/*
* escapeWideCString already throws an exception,
* no need to throw a second one here.
*/
free(xml);
return NULL;
}
if (OF_UNLIKELY(!xf_resize_wchars(&xml, &len, 1 + wcslen(arg) +
2 + wcslen(esc_val) + 1))) {
free(esc_val);
@throw [OFNoMemException
newWithObject: nil
andSize: (len + 1 + wcslen(arg) + 2 +
wcslen(esc_val) + 1) *
sizeof(wchar_t)];
}
xml[i++] = L' ';
wmemcpy(xml + i, arg, wcslen(arg));
i += wcslen(arg);
xml[i++] = L'=';
xml[i++] = L'\'';
wmemcpy(xml + i, esc_val, wcslen(esc_val));
i += wcslen(esc_val);
xml[i++] = L'\'';
free(esc_val);
}
va_end(args);
/* End of tag */
if (close) {
if (data == NULL) {
if (!xf_resize_wchars(&xml, &len, 2 - 1))
@throw [OFNoMemException
newWithObject: nil
andSize: (len + 2 - 1) *
sizeof(wchar_t)];
xml[i++] = L'/';
xml[i++] = L'>';
} else {
if (!xf_resize_wchars(&xml, &len, 1 + wcslen(data) +
2 + wcslen(name) + 1 - 1))
@throw [OFNoMemException
newWithObject: nil
andSize: (len + 1 + wcslen(data) + 2 +
wcslen(name) + 1 - 1) *
sizeof(wchar_t)];
xml[i++] = L'>';
wmemcpy(xml + i, data, wcslen(data));
i += wcslen(data);
xml[i++] = L'<';
xml[i++] = L'/';
wmemcpy(xml + i, name, wcslen(name));
i += wcslen(name);
xml[i++] = L'>';
}
} else
xml[i++] = L'>';
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);
if (SIZE_MAX - len < 1)
@throw [OFOutOfRangeException newWithObject: nil];
len++;
if ((ret = malloc(len)) == NULL)
@throw [OFNoMemException newWithObject: nil
andSize: len];
memcpy(ret, strs[0], len - 1);
pos = len - 1;
for (i = 1; strs[i] != NULL; i++) {
if (OF_UNLIKELY(!xf_add2chars(&ret, &len, &pos, strs[i]))) {
free(ret);
@throw [OFNoMemException
newWithObject: nil
andSize: len + strlen(strs[i])];
}
}
for (i = 0; strs[i] != NULL; i++)
free(strs[i]);
ret[pos] = 0;
return ret;
}
+ (wchar_t*)concatAndFreeWideCStrings: (wchar_t**)strs
{
wchar_t *ret;
size_t i, len, pos;
if (strs[0] == NULL)
return NULL;
len = wcslen(*strs);
if (SIZE_MAX - len < 1)
@throw [OFOutOfRangeException newWithObject: nil];
len++;
if (len > SIZE_MAX - sizeof(wchar_t))
@throw [OFOutOfRangeException newWithObject: nil];
if ((ret = malloc(len * sizeof(wchar_t))) == NULL)
@throw [OFNoMemException newWithObject: nil
andSize: len * sizeof(wchar_t)];
wmemcpy(ret, strs[0], len - 1);
pos = len - 1;
for (i = 1; strs[i] != NULL; i++) {
if (!xf_add2wchars(&ret, &len, &pos, strs[i])) {
free(ret);
@throw [OFNoMemException
newWithObject: nil
andSize: (wcslen(strs[i]) + len) *
sizeof(wchar_t)];
}
}
for (i = 0; strs[i] != NULL; i++)
free(strs[i]);
ret[pos] = 0;
return ret;
}
@end