2011-06-21 19:31:46 +03:00
|
|
|
/*
|
|
|
|
Copyright (C) 2011 Markus Kauppila <markus.kauppila@gmail.com>
|
|
|
|
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
|
|
warranty. In no event will the authors be held liable for any damages
|
|
|
|
arising from the use of this software.
|
|
|
|
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
|
|
including commercial applications, and to alter it and redistribute it
|
|
|
|
freely, subject to the following restrictions:
|
|
|
|
|
|
|
|
1. The origin of this software must not be misrepresented; you must not
|
|
|
|
claim that you wrote the original software. If you use this software
|
|
|
|
in a product, an acknowledgment in the product documentation would be
|
|
|
|
appreciated but is not required.
|
|
|
|
2. Altered source versions must be plainly marked as such, and must not be
|
|
|
|
misrepresented as being the original software.
|
|
|
|
3. This notice may not be removed or altered from any source distribution.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
#include <SDL/SDL.h>
|
|
|
|
|
|
|
|
#include "xml.h"
|
|
|
|
|
|
|
|
/*! Points the function which handles the output */
|
|
|
|
static LogOutputFp logger = 0;
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* Defines structure used for "counting" open XML-tags
|
|
|
|
*/
|
|
|
|
typedef struct TagList {
|
|
|
|
const char *tag;
|
|
|
|
struct TagList *next;
|
|
|
|
} TagList;
|
|
|
|
|
|
|
|
static TagList *openTags = NULL;
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* Prepend the open tags list
|
|
|
|
*
|
|
|
|
* \return On error returns non-zero value, otherwise zero will returned
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
AddOpenTag(const char *tag)
|
|
|
|
{
|
|
|
|
TagList *openTag = SDL_malloc(sizeof(TagList));
|
|
|
|
if(openTag == NULL) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
memset(openTag, 0, sizeof(TagList));
|
|
|
|
|
2011-06-24 14:35:14 +03:00
|
|
|
const int tagSize = SDL_strlen(tag) + 1;
|
|
|
|
openTag->tag = SDL_malloc(tagSize);
|
2011-06-26 23:04:37 +03:00
|
|
|
strncpy((char *)openTag->tag, (char *)tag, tagSize);
|
2011-06-24 14:35:14 +03:00
|
|
|
|
2011-06-21 19:31:46 +03:00
|
|
|
openTag->next = openTags;
|
|
|
|
|
|
|
|
openTags = openTag;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* Removes the first tag from the open tag list
|
|
|
|
*
|
|
|
|
* \return On error returns non-zero value, otherwise zero will returned
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
RemoveOpenTag(const char *tag)
|
|
|
|
{
|
|
|
|
if(openTags == NULL) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int retVal = 0;
|
|
|
|
|
2011-06-24 14:35:14 +03:00
|
|
|
const int size = SDL_strlen(tag);
|
|
|
|
char *tempTag = SDL_malloc(size);
|
|
|
|
strncpy(tempTag, tag, size);
|
|
|
|
|
2011-06-21 19:31:46 +03:00
|
|
|
// Tag should always be the same as previously opened tag
|
|
|
|
// It prevents opening and ending tag mismatch
|
2011-06-24 14:35:14 +03:00
|
|
|
if(SDL_strcmp(tempTag, tag) == 0) {
|
2011-06-21 19:31:46 +03:00
|
|
|
TagList *openTag = openTags;
|
2011-06-26 23:04:37 +03:00
|
|
|
SDL_free((char *)openTag->tag);
|
2011-06-21 19:31:46 +03:00
|
|
|
|
2011-06-24 14:35:14 +03:00
|
|
|
/*
|
|
|
|
int counter = 0;
|
|
|
|
for(; counter < strlen(buffer); ++counter) {
|
|
|
|
buffer[counter] = tolower(buffer[counter]);
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
openTags = openTags->next;
|
|
|
|
SDL_free(openTag);
|
2011-06-21 19:31:46 +03:00
|
|
|
} else {
|
2011-06-24 14:35:14 +03:00
|
|
|
//printf("Debug | xml.c:RemoveOpenTag(): open/end tag mismatch");
|
2011-06-21 19:31:46 +03:00
|
|
|
retVal = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return retVal;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* Debug function. Prints the contents of the open tags list.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
PrintOpenTags()
|
|
|
|
{
|
|
|
|
printf("\nOpen tags:\n");
|
|
|
|
|
|
|
|
TagList *openTag = NULL;
|
|
|
|
for(openTag = openTags; openTag; openTag = openTag->next) {
|
|
|
|
printf("\ttag: %s\n", openTag->tag);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-22 21:56:23 +03:00
|
|
|
|
|
|
|
/*!
|
|
|
|
* Converts the special characters ', ", <, >, and & to
|
|
|
|
* corresponding entities: ' " < > and &
|
|
|
|
*
|
|
|
|
* \param string String to be escaped
|
|
|
|
* \return Escaped string
|
|
|
|
*/
|
|
|
|
const char *EscapeString(const char *string) {
|
|
|
|
const int bufferSize = 4096;
|
|
|
|
char buffer[bufferSize];
|
|
|
|
memset(buffer, 0, bufferSize);
|
|
|
|
|
|
|
|
// prevents the code doing a 'bus error'
|
2011-06-23 22:00:03 +03:00
|
|
|
char *stringBuffer = SDL_malloc(bufferSize);
|
2011-06-22 21:56:23 +03:00
|
|
|
strncpy(stringBuffer, string, bufferSize);
|
|
|
|
|
|
|
|
// Ampersand (&) must be first, otherwise it'll mess up the other entities
|
|
|
|
char *characters[] = {"&", "'", "\"", "<", ">"};
|
|
|
|
char *entities[] = {"&", "'", """, "<", ">"};
|
|
|
|
int maxCount = 5;
|
|
|
|
|
|
|
|
int counter = 0;
|
|
|
|
for(; counter < maxCount; ++counter) {
|
|
|
|
char *character = characters[counter];
|
|
|
|
char *entity = entities[counter];
|
|
|
|
|
|
|
|
if(strstr(stringBuffer, character) == NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
char *token = strtok(stringBuffer, character);
|
|
|
|
while(token) {
|
|
|
|
char *nextToken = strtok(NULL, character);
|
|
|
|
|
|
|
|
//! \todo use strncat and count the bytes left in the buffer
|
|
|
|
strcat(buffer, token);
|
|
|
|
if(nextToken)
|
|
|
|
strcat(buffer, entity);
|
|
|
|
|
|
|
|
token = nextToken;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(stringBuffer, buffer, bufferSize);
|
|
|
|
memset(buffer, 0, bufferSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
return stringBuffer;
|
|
|
|
}
|
|
|
|
|
2011-06-24 14:35:14 +03:00
|
|
|
/*! Turns all the characters of the given
|
|
|
|
* string to lowercase and returns the resulting string.
|
|
|
|
*
|
|
|
|
* \param string String to be converted
|
|
|
|
* \return Lower-case version of the given string
|
|
|
|
*/
|
|
|
|
char *
|
2011-06-26 23:04:37 +03:00
|
|
|
ToLowerCase(const char *string)
|
2011-06-24 14:35:14 +03:00
|
|
|
{
|
|
|
|
const int size = SDL_strlen(string);
|
|
|
|
char *ret = SDL_malloc(size + 1);
|
|
|
|
strncpy(ret, string, size);
|
|
|
|
ret[size] = '\0';
|
|
|
|
|
|
|
|
// turn the tag to lower case for case-insensitive comparation
|
|
|
|
int counter = 0;
|
|
|
|
for(; counter < size; ++counter) {
|
|
|
|
ret[counter] = tolower(ret[counter]);
|
|
|
|
}
|
2011-06-22 21:56:23 +03:00
|
|
|
|
2011-06-24 14:35:14 +03:00
|
|
|
// printf("Debug: %s == %s\n", string, ret);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2011-06-22 21:56:23 +03:00
|
|
|
|
2011-06-21 19:31:46 +03:00
|
|
|
/*
|
|
|
|
===================
|
|
|
|
|
2011-06-22 17:41:37 +03:00
|
|
|
Functions to handle creation of XML elements
|
2011-06-21 19:31:46 +03:00
|
|
|
|
|
|
|
===================
|
|
|
|
*/
|
|
|
|
|
2011-06-21 22:04:44 +03:00
|
|
|
static const char *root;
|
2011-06-21 19:31:46 +03:00
|
|
|
|
2011-06-22 18:13:06 +03:00
|
|
|
/*! Size for xml element buffer */
|
|
|
|
#define bufferSize 1024
|
|
|
|
/*! Buffer for storing the xml element under construction */
|
|
|
|
static char buffer[bufferSize];
|
|
|
|
|
2011-06-23 22:00:03 +03:00
|
|
|
char *
|
|
|
|
XMLOpenDocument(const char *rootTag)
|
2011-06-21 19:31:46 +03:00
|
|
|
{
|
2011-06-23 22:00:03 +03:00
|
|
|
const char *doctype = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
|
2011-06-21 19:31:46 +03:00
|
|
|
|
2011-06-22 18:13:06 +03:00
|
|
|
memset(buffer, 0, bufferSize);
|
|
|
|
snprintf(buffer, bufferSize, "<%s>", rootTag);
|
2011-06-21 19:31:46 +03:00
|
|
|
|
|
|
|
AddOpenTag(rootTag);
|
2011-06-21 22:04:44 +03:00
|
|
|
|
|
|
|
root = rootTag; // it's fine, as long as rootTag points to static memory?
|
2011-06-23 22:00:03 +03:00
|
|
|
|
|
|
|
const int doctypeSize = SDL_strlen(doctype);
|
|
|
|
const int tagSize = SDL_strlen(buffer);
|
|
|
|
|
|
|
|
const int size = doctypeSize + tagSize + 1; // extra byte for '\0'
|
|
|
|
char *ret = SDL_malloc(size);
|
|
|
|
// copy doctype
|
|
|
|
strncpy(ret, doctype, doctypeSize);
|
|
|
|
// copy tag
|
|
|
|
strncpy(ret + doctypeSize, buffer, tagSize);
|
|
|
|
ret[size] = '\0';
|
|
|
|
return ret;
|
2011-06-21 19:31:46 +03:00
|
|
|
}
|
|
|
|
|
2011-06-23 22:00:03 +03:00
|
|
|
char *
|
2011-06-21 19:31:46 +03:00
|
|
|
XMLCloseDocument() {
|
2011-06-23 22:00:03 +03:00
|
|
|
return XMLCloseElement(root);
|
2011-06-21 19:31:46 +03:00
|
|
|
}
|
|
|
|
|
2011-06-23 22:00:03 +03:00
|
|
|
char *
|
2011-06-21 19:31:46 +03:00
|
|
|
XMLOpenElement(const char *tag)
|
|
|
|
{
|
2011-06-22 18:13:06 +03:00
|
|
|
memset(buffer, 0, bufferSize);
|
|
|
|
snprintf(buffer, bufferSize, "<%s>", tag);
|
2011-06-21 19:31:46 +03:00
|
|
|
|
|
|
|
AddOpenTag(tag);
|
2011-06-23 22:00:03 +03:00
|
|
|
|
|
|
|
const int size = SDL_strlen(buffer);
|
|
|
|
char *ret = SDL_malloc(size + 1);
|
|
|
|
strncpy(ret, buffer, size);
|
|
|
|
ret[size] = '\0';
|
|
|
|
|
|
|
|
return ret;
|
2011-06-21 19:31:46 +03:00
|
|
|
}
|
|
|
|
|
2011-06-23 22:00:03 +03:00
|
|
|
char *
|
2011-06-21 19:31:46 +03:00
|
|
|
XMLAddContent(const char *content)
|
|
|
|
{
|
2011-06-22 21:56:23 +03:00
|
|
|
const char *escapedContent = EscapeString(content);
|
|
|
|
|
2011-06-22 18:13:06 +03:00
|
|
|
memset(buffer, 0, bufferSize);
|
2011-06-22 21:56:23 +03:00
|
|
|
snprintf(buffer, bufferSize, "%s", escapedContent);
|
2011-06-23 22:00:03 +03:00
|
|
|
SDL_free((char *)escapedContent);
|
|
|
|
|
|
|
|
const int size = SDL_strlen(buffer);
|
|
|
|
char *ret = SDL_malloc(size + 1);
|
|
|
|
strncpy(ret, buffer, size);
|
|
|
|
ret[size] = '\0';
|
|
|
|
|
|
|
|
return ret;
|
2011-06-22 18:13:06 +03:00
|
|
|
}
|
2011-06-21 19:31:46 +03:00
|
|
|
|
2011-06-23 22:00:03 +03:00
|
|
|
char *
|
2011-06-21 19:31:46 +03:00
|
|
|
XMLCloseElement(const char *tag)
|
|
|
|
{
|
2011-06-23 22:00:03 +03:00
|
|
|
char *ret = SDL_malloc(bufferSize);
|
|
|
|
memset(ret, 0, bufferSize);
|
|
|
|
|
2011-06-21 19:31:46 +03:00
|
|
|
// Close the open tags with proper nesting. Closes tags until it finds
|
|
|
|
// the given tag which is the last tag that will be closed
|
|
|
|
TagList *openTag = openTags;
|
|
|
|
while(openTag) {
|
|
|
|
TagList *temp = openTag->next;
|
|
|
|
|
2011-06-24 14:35:14 +03:00
|
|
|
char *lowOpenTag = ToLowerCase(openTag->tag);
|
|
|
|
char *lowTag = ToLowerCase(tag);
|
2011-06-23 22:00:03 +03:00
|
|
|
|
2011-06-24 14:35:14 +03:00
|
|
|
const int openTagSize = SDL_strlen(lowOpenTag);
|
|
|
|
const int tagSize = SDL_strlen(lowTag);
|
2011-06-21 19:31:46 +03:00
|
|
|
const int compSize = (openTagSize > tagSize) ? openTagSize : tagSize;
|
|
|
|
|
2011-06-24 14:35:14 +03:00
|
|
|
memset(buffer, 0, bufferSize);
|
|
|
|
|
2011-06-21 19:31:46 +03:00
|
|
|
int breakOut = 0;
|
2011-06-24 14:35:14 +03:00
|
|
|
if(SDL_strncmp(lowOpenTag, lowTag, compSize) == 0) {
|
2011-06-21 19:31:46 +03:00
|
|
|
breakOut = 1;
|
2011-06-24 14:35:14 +03:00
|
|
|
snprintf(buffer, bufferSize, "</%s>", tag);
|
|
|
|
} else {
|
|
|
|
snprintf(buffer, bufferSize, "</%s>", openTag->tag);
|
2011-06-21 19:31:46 +03:00
|
|
|
}
|
|
|
|
|
2011-06-24 14:35:14 +03:00
|
|
|
SDL_free(lowOpenTag);
|
|
|
|
SDL_free(lowTag);
|
|
|
|
|
|
|
|
// \todo use strNcat
|
|
|
|
strcat(ret, buffer);
|
|
|
|
|
2011-06-21 19:31:46 +03:00
|
|
|
RemoveOpenTag(openTag->tag);
|
|
|
|
|
|
|
|
openTag = temp;
|
|
|
|
|
|
|
|
if(breakOut) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2011-06-23 22:00:03 +03:00
|
|
|
|
|
|
|
return ret;
|
2011-06-21 19:31:46 +03:00
|
|
|
}
|
2011-06-21 22:04:44 +03:00
|
|
|
|