Added ultra-basic XML support in Jinete

master
David Capello 2007-09-27 18:07:21 +00:00
parent a68a9e2e56
commit f7657bb2dd
7 changed files with 1335 additions and 0 deletions

30
docs/licenses/BSD.txt Normal file
View File

@ -0,0 +1,30 @@
Jinete - a GUI library
Copyright (c) 2003, 2004, 2005, 2007, David A. Capello
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of the Jinete nor the names of its contributors may
be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

18
docs/licenses/ZLIB.txt Normal file
View File

@ -0,0 +1,18 @@
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.

129
jinete/examples/22xml.c Normal file
View File

@ -0,0 +1,129 @@
/* jinete - a GUI library
* Copyright (C) 2003-2005, 2007 by David A. Capello
*
* Jinete is gift-ware.
*/
#include <allegro.h>
#include "jinete.h"
static char *xml_src =
"<root>" "\n"
" <item>" "\n"
" <value1>text" "\n"
" </value1>" "\n"
" more text" "\n"
" </item>" "\n"
"</root>";
static JWidget textsrc, textdst;
static void read_xml(JWidget widget);
static void prt_xmlnode(JXmlNode node, int indent);
static void prt(const char *format, ...);
int main(int argc, char *argv[])
{
JWidget manager, window, hbox, vbox, button;
allegro_init();
if (set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0) < 0) {
if (set_gfx_mode(GFX_AUTODETECT, 640, 480, 0, 0) < 0) {
allegro_message("%s\n", allegro_error);
return 1;
}
}
install_timer();
install_keyboard();
install_mouse();
manager = jmanager_new();
ji_set_standard_theme();
window = jwindow_new("XML");
hbox = jpanel_new(JI_VERTICAL);
vbox = jbox_new(JI_VERTICAL);
textsrc = jtextbox_new(xml_src, JI_WORDWRAP);
textdst = jtextbox_new("", JI_WORDWRAP);
button = jbutton_new("Read XML");
jbutton_add_command(button, read_xml);
jwidget_expansive(hbox, TRUE);
jwidget_expansive(textsrc, TRUE);
jwidget_expansive(textdst, TRUE);
jwidget_set_static_size(textsrc, 600, 200);
jwidget_set_static_size(textdst, 600, 200);
jwidget_add_child(hbox, textsrc);
jwidget_add_child(hbox, textdst);
jwidget_add_child(vbox, hbox);
jwidget_add_child(vbox, button);
jwidget_add_child(window, vbox);
jwindow_open_bg(window);
jmanager_run(manager);
jmanager_free(manager);
return 0;
}
END_OF_MAIN();
static void read_xml(JWidget widget)
{
JXml xml;
prt("Processing XML...\n\n");
xml = jxml_new_from_string(jwidget_get_text(textsrc));
if (!xml) {
prt("ERROR\n");
return;
}
prt_xmlnode((JXmlNode)xml->root, 0);
jxml_free(xml);
}
static void prt_xmlnode(JXmlNode node, int indent)
{
JLink link;
int c;
for (c=0; c<indent; c++)
prt(" ");
prt("JXmlNode->value = %s\n", node->value);
JI_LIST_FOR_EACH(node->children, link)
prt_xmlnode(link->data, indent+1);
}
static void prt(const char *format, ...)
{
const char *text;
char buf[1024];
char *final;
va_list ap;
va_start(ap, format);
uvsprintf(buf, format, ap);
va_end(ap);
text = jwidget_get_text(textdst);
if (!text)
final = jstrdup(buf);
else {
final = jmalloc(ustrlen(text) + ustrlen(buf) + 1);
ustrcpy(final, empty_string);
ustrcat(final, text);
ustrcat(final, buf);
}
jwidget_set_text(textdst, final);
jfree(final);
}

View File

@ -0,0 +1,70 @@
/* Jinete - a GUI library
* Copyright (c) 2007 David A. Capello
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of the Jinete nor the names of its contributors may
* be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef JINETE_STREAM_H
#define JINETE_STREAM_H
#include "jinete/base.h"
#include <stdio.h>
JI_BEGIN_DECLS
struct jstream
{
void (*close)(JStream stream);
bool (*eof)(JStream stream);
void (*flush)(JStream stream);
int (*getc)(JStream stream);
char *(*gets)(JStream stream, char *s, int size);
int (*putc)(JStream stream, int ch);
int (*seek)(JStream stream, int offset, int whence);
int (*tell)(JStream stream);
/* void (*error)(JStream stream, const char *err); */
};
JStream jstream_new(int size);
JStream jstream_new_for_file(FILE *f);
JStream jstream_new_for_string(const char *string);
void jstream_free(JStream stream);
bool jstream_eof(JStream stream);
void jstream_flush(JStream stream);
int jstream_getc(JStream stream);
char *jstream_gets(JStream stream, char *s, int size);
int jstream_putc(JStream stream, int ch);
/* int jstream_puts(JStream stream, const char *s); */
int jstream_seek(JStream stream, int offset, int whence);
int jstream_tell(JStream stream);
JI_END_DECLS
#endif /* JINETE_STREAM_H */

127
jinete/include/jinete/xml.h Normal file
View File

@ -0,0 +1,127 @@
/* Jinete - a GUI library
* Copyright (c) 2007 David A. Capello
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of the Jinete nor the names of its contributors may
* be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef JINETE_XML_H
#define JINETE_XML_H
#include "jinete/base.h"
JI_BEGIN_DECLS
enum { JI_XML_ELEM, JI_XML_TEXT };
struct jxml
{
JXmlElem root;
};
struct jxmlattr
{
char *name;
char *value;
};
struct jxmlnode
{
int type;
char *value;
JXmlNode parent;
JList children;
};
struct jxmlelem
{
struct jxmlnode head;
JList attributes;
};
struct jxmltext
{
struct jxmlnode head;
};
/* JXml **********************************************/
JXml jxml_new(void);
JXml jxml_new_from_file(const char *filename);
JXml jxml_new_from_string(const char *buffer);
JXml jxml_new_from_stream(JStream stream);
void jxml_free(JXml xml);
JXmlElem jxml_get_root(JXml xml);
JXmlElem jxml_get_elem_by_id(JXml xml, const char *id);
/* JXmlAttr ******************************************/
JXmlAttr jxmlattr_new(const char *name, const char *value);
void jxmlattr_free(JXmlAttr attr);
const char *jxmlattr_get_name(JXmlAttr attr);
const char *jxmlattr_get_value(JXmlAttr attr);
void jxmlattr_set_name(JXmlAttr attr, const char *name);
void jxmlattr_set_value(JXmlAttr attr, const char *value);
/* JXmlNode ******************************************/
JXmlNode jxmlnode_new(int type, const char *value);
void jxmlnode_free(JXmlNode node);
const char *jxmlnode_get_value(JXmlNode node);
void jxmlnode_set_value(JXmlNode node, const char *value);
void jxmlnode_add_child(JXmlNode node, JXmlNode child);
void jxmlnode_remove_child(JXmlNode node, JXmlNode child);
/* JXmlElem ******************************************/
JXmlElem jxmlelem_new(const char *name);
void jxmlelem_free(JXmlElem elem);
const char *jxmlelem_get_name(JXmlElem elem);
void jxmlelem_set_name(JXmlElem elem, const char *name);
bool jxmlelem_has_attr(JXmlElem elem, const char *name);
const char *jxmlelem_get_attr(JXmlElem elem, const char *name);
void jxmlelem_set_attr(JXmlElem elem, const char *name, const char *value);
JXmlElem jxmlelem_get_elem_by_id(JXmlElem elem, const char *id);
/* JXmlText ******************************************/
JXmlText jxmltext_new(const char *value);
void jxmltext_free(JXmlText text);
const char *jxmltext_get_text(JXmlText text);
void jxmltext_set_text(JXmlText text, const char *value);
JI_END_DECLS
#endif /* JINETE_XML_H */

346
jinete/src/jstream.c Normal file
View File

@ -0,0 +1,346 @@
/* Jinete - a GUI library
* Copyright (c) 2007 David A. Capello
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of the Jinete nor the names of its contributors may
* be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* jstream_string is based on streams of HTMLEX */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "jinete.h"
/* jinete stream file/string */
#define JSF ((struct jstream_file *)(stream))
#define JSS ((struct jstream_string *)(stream))
/* size of chunks to add to the buffer for jstream_strings */
#define BLOCKSIZE 1024
struct jstream_file
{
struct jstream head;
FILE *file;
};
struct jstream_string
{
struct jstream head;
char *buf; /* buffer for the characters */
int size; /* real size of the buffer */
int end; /* index of the last character used in the buffer */
int pos; /* the position for read data */
};
static void stream_file_close(JStream stream);
static bool stream_file_eof(JStream stream);
static void stream_file_flush(JStream stream);
static int stream_file_getc(JStream stream);
static char *stream_file_gets(JStream stream, char *s, int size);
static int stream_file_putc(JStream stream, int ch);
static int stream_file_seek(JStream stream, int offset, int whence);
static int stream_file_tell(JStream stream);
static void stream_string_close(JStream stream);
static bool stream_string_eof(JStream stream);
static void stream_string_flush(JStream stream);
static int stream_string_getc(JStream stream);
static char *stream_string_gets(JStream stream, char *s, int size);
static int stream_string_putc(JStream stream, int ch);
static int stream_string_seek(JStream stream, int offset, int whence);
static int stream_string_tell(JStream stream);
JStream jstream_new(int size)
{
JStream stream = jmalloc0(MAX(size, sizeof(struct jstream)));
if (!stream)
return NULL;
return stream;
}
JStream jstream_new_for_file(FILE *f)
{
JStream stream = jstream_new(sizeof(struct jstream_file));
if (!stream)
return NULL;
stream->close = stream_file_close;
stream->eof = stream_file_eof;
stream->flush = stream_file_flush;
stream->getc = stream_file_getc;
stream->gets = stream_file_gets;
stream->putc = stream_file_putc;
stream->seek = stream_file_seek;
stream->tell = stream_file_tell;
JSF->file = f;
return stream;
}
JStream jstream_new_for_string(const char *buffer)
{
JStream stream = jstream_new(sizeof(struct jstream_string));
if (!stream)
return NULL;
stream->close = stream_string_close;
stream->eof = stream_string_eof;
stream->flush = stream_string_flush;
stream->getc = stream_string_getc;
stream->gets = stream_string_gets;
stream->putc = stream_string_putc;
stream->seek = stream_string_seek;
stream->tell = stream_string_tell;
JSS->buf = buffer ? jstrdup(buffer): NULL;
if (buffer && !JSS->buf) {
jfree(stream);
return NULL;
}
JSS->end = buffer ? strlen(buffer): 0;
JSS->size = buffer ? ((JSS->end / BLOCKSIZE) +
((JSS->end % BLOCKSIZE) > 0 ? 1: 0)): 0;
JSS->pos = 0;
return stream;
}
void jstream_free(JStream stream)
{
if (stream->close)
(*stream->close)(stream);
jfree(stream);
}
bool jstream_eof(JStream stream)
{
if (stream->eof)
return (*stream->eof)(stream);
else
return TRUE;
}
void jstream_flush(JStream stream)
{
if (stream->flush)
(*stream->flush)(stream);
}
int jstream_getc(JStream stream)
{
if (stream->getc)
return (*stream->getc)(stream);
else
return EOF;
}
char *jstream_gets(JStream stream, char *s, int size)
{
if (stream->gets)
return (*stream->gets)(stream, s, size);
else
return NULL;
}
int jstream_putc(JStream stream, int ch)
{
if (stream->putc)
return (*stream->putc)(stream, ch);
else
return EOF;
}
/* int jstream_puts(JStream stream, const char *s) */
/* { */
/* return 0; */
/* } */
int jstream_seek(JStream stream, int offset, int whence)
{
if (stream->seek)
return (*stream->seek)(stream, offset, whence);
else
return EOF;
}
int jstream_tell(JStream stream)
{
if (stream->tell)
return (*stream->tell)(stream);
else
return EOF;
}
/**********************************************************************/
/* jstream_file */
static void stream_file_close(JStream stream)
{
FILE *f = JSF->file;
if ((f != stdin) && (f != stdout) && (f != stderr))
fclose(f);
}
static bool stream_file_eof(JStream stream)
{
return feof(JSF->file) != 0;
}
static void stream_file_flush(JStream stream)
{
fflush(JSF->file);
}
static int stream_file_getc(JStream stream)
{
return fgetc(JSF->file);
}
static char *stream_file_gets(JStream stream, char *s, int size)
{
return fgets(s, size, JSF->file);
}
static int stream_file_putc(JStream stream, int ch)
{
return fputc(ch, JSF->file);
}
static int stream_file_seek(JStream stream, int offset, int whence)
{
return fseek(JSF->file, offset, whence);
}
static int stream_file_tell(JStream stream)
{
return ftell(JSF->file);
}
/**********************************************************************/
/* jstream_string */
static void stream_string_close(JStream stream)
{
if (JSS->buf)
jfree(JSS->buf);
}
static bool stream_string_eof(JStream stream)
{
return (JSS->pos == JSS->end);
}
static void stream_string_flush(JStream stream)
{
/* do nothing */
}
static int stream_string_getc(JStream stream)
{
if (JSS->pos < JSS->end)
return JSS->buf[JSS->pos++];
else
return EOF;
}
static char *stream_string_gets(JStream stream, char *s, int size)
{
if (JSS->pos == JSS->end)
return NULL;
else {
char *r = s;
int c, i;
for (i = 0; i < size; ++i) {
if (JSS->pos < JSS->end) {
c = JSS->buf[JSS->pos++];
*(s++) = c;
if (c == '\n')
break;
}
else
break;
}
*s = 0;
return r;
}
}
static int stream_string_putc(JStream stream, int ch)
{
if (JSS->end >= JSS->size) {
JSS->size += BLOCKSIZE;
JSS->buf = jrealloc(JSS->buf, JSS->size);
if (!JSS->buf) {
JSS->size = 0;
JSS->end = 0;
JSS->pos = 0;
return EOF;
}
}
if (JSS->pos < JSS->end)
memmove(JSS->buf + JSS->pos + 1,
JSS->buf + JSS->pos,
JSS->end - JSS->pos);
JSS->end++;
return JSS->buf[JSS->pos++] = ch;
}
static int stream_string_seek(JStream stream, int offset, int whence)
{
switch (whence) {
case SEEK_SET:
JSS->pos = offset;
break;
case SEEK_CUR:
JSS->pos += offset;
break;
case SEEK_END:
JSS->pos = JSS->end + offset;
break;
default:
return -1;
}
if (JSS->pos < 0)
JSS->pos = 0;
else if (JSS->pos > JSS->end)
JSS->pos = JSS->end;
return 0;
}
static int stream_string_tell(JStream stream)
{
return JSS->pos;
}

615
jinete/src/jxml.c Normal file
View File

@ -0,0 +1,615 @@
/* Jinete - a GUI library
* Copyright (c) 2007 David A. Capello
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of the Jinete nor the names of its contributors may
* be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "jinete.h"
/* determine is the characer is a blank space */
#define IS_BLANK(chr) (((chr) == ' ') || \
((chr) == '\t') || \
((chr) == '\n') || \
((chr) == '\r'))
static JList read_nodes(JStream stream);
static JXmlElem read_elem(char *elem_string);
/**********************************************************************/
/* JXml */
/**********************************************************************/
JXml jxml_new(void)
{
JXml xml= jnew(struct jxml, 1);
if (!xml)
return NULL;
xml->root = NULL;
return xml;
}
JXml jxml_new_from_file(const char *filename)
{
JStream stream;
JXml xml;
FILE *f;
/* open the file */
f = fopen(filename, "rt");
if (!f)
return NULL;
/* read the xml */
stream = jstream_new_for_file(f);
xml = jxml_new_from_stream(stream);
jstream_free(stream);
/* and close it */
fclose(f);
return xml;
}
JXml jxml_new_from_string(const char *buffer)
{
JStream stream;
JXml xml;
stream = jstream_new_for_string(buffer);
xml = jxml_new_from_stream(stream);
jstream_free(stream);
return xml;
}
JXml jxml_new_from_stream(JStream stream)
{
JList nodes = read_nodes(stream);
JXml xml = NULL;
if (!nodes)
return NULL;
if (jlist_length(nodes) != 1) {
JLink link;
JI_LIST_FOR_EACH(nodes, link) {
JXmlNode node = (JXmlNode)link->data;
jxmlnode_free(node);
}
/* xml_error("More than one tag in root"); */
}
else {
xml = jxml_new();
xml->root = jlist_first_data(nodes);
}
jlist_free(nodes);
return xml;
}
void jxml_free(JXml xml)
{
if (xml->root)
jxmlelem_free(xml->root);
jfree(xml);
}
JXmlElem jxml_get_root(JXml xml)
{
return xml->root;
}
JXmlElem jxml_get_elem_by_id(JXml xml, const char *id)
{
if (xml->root)
return jxmlelem_get_elem_by_id(xml->root, id);
else
return NULL;
}
/**********************************************************************/
/* JXmlAttr */
/**********************************************************************/
JXmlAttr jxmlattr_new(const char *name, const char *value)
{
JXmlAttr attr = jnew(struct jxmlattr, 1);
attr->name = name ? jstrdup(name): NULL;
attr->value = value ? jstrdup(value): NULL;
return attr;
}
void jxmlattr_free(JXmlAttr attr)
{
if (attr->name)
jfree(attr->name);
if (attr->value)
jfree(attr->value);
jfree(attr);
}
const char *jxmlattr_get_name(JXmlAttr attr)
{
return attr->name;
}
const char *jxmlattr_get_value(JXmlAttr attr)
{
return attr->value;
}
void jxmlattr_set_name(JXmlAttr attr, const char *name)
{
if (attr->name)
jfree(attr->name);
attr->name = name ? jstrdup(name): NULL;
}
void jxmlattr_set_value(JXmlAttr attr, const char *value)
{
if (attr->value)
jfree(attr->value);
attr->value = value ? jstrdup(value): NULL;
}
/**********************************************************************/
/* JXmlNode */
/**********************************************************************/
JXmlNode jxmlnode_new(int type, const char *value)
{
JXmlNode node = NULL;
switch (type) {
case JI_XML_ELEM:
node = (JXmlNode)jnew(struct jxmlelem, 1);
if (node) {
((JXmlElem)node)->attributes = jlist_new();
}
break;
case JI_XML_TEXT:
node = (JXmlNode)jnew(struct jxmltext, 1);
break;
}
if (node) {
node->type = type;
node->value = value ? jstrdup(value): NULL;
node->parent = NULL;
node->children = jlist_new();
}
return node;
}
void jxmlnode_free(JXmlNode node)
{
JLink link;
switch (node->type) {
case JI_XML_ELEM:
JI_LIST_FOR_EACH(((JXmlElem)node)->attributes, link) {
JXmlAttr child = (JXmlAttr)link->data;
jxmlattr_free(child);
}
jlist_free(((JXmlElem)node)->attributes);
break;
case JI_XML_TEXT:
/* do nothing */
break;
}
if (node->value)
jfree(node->value);
JI_LIST_FOR_EACH(node->children, link) {
JXmlNode child = (JXmlNode)link->data;
jxmlnode_free(child);
}
jlist_free(node->children);
jfree(node);
}
const char *jxmlnode_get_value(JXmlNode node)
{
return node->value;
}
void jxmlnode_set_value(JXmlNode node, const char *value)
{
if (node->value)
jfree(node->value);
node->value = value ? jstrdup(value): NULL;
}
void jxmlnode_add_child(JXmlNode node, JXmlNode child)
{
jlist_append(node->children, child);
child->parent = node;
}
void jxmlnode_remove_child(JXmlNode node, JXmlNode child)
{
jlist_remove(node->children, child);
child->parent = NULL;
}
/**********************************************************************/
/* JXmlElem */
/**********************************************************************/
JXmlElem jxmlelem_new(const char *name)
{
return (JXmlElem)jxmlnode_new(JI_XML_ELEM, name);
}
void jxmlelem_free(JXmlElem elem)
{
jxmlnode_free((JXmlNode)elem);
}
const char *jxmlelem_get_name(JXmlElem elem)
{
return jxmlnode_get_value((JXmlNode)elem);
}
void jxmlelem_set_name(JXmlElem elem, const char *name)
{
jxmlnode_set_value((JXmlNode)elem, name);
}
bool jxmlelem_has_attr(JXmlElem elem, const char *name)
{
JLink link;
JI_LIST_FOR_EACH(elem->attributes, link) {
JXmlAttr attr = (JXmlAttr)link->data;
if (strcmp(name, jxmlattr_get_name(attr)) == 0)
return TRUE;
}
return FALSE;
}
const char *jxmlelem_get_attr(JXmlElem elem, const char *name)
{
JLink link;
JI_LIST_FOR_EACH(elem->attributes, link) {
JXmlAttr attr = (JXmlAttr)link->data;
if (strcmp(name, jxmlattr_get_name(attr)) == 0) {
return jxmlattr_get_value(attr);
}
}
return NULL;
}
void jxmlelem_set_attr(JXmlElem elem, const char *name, const char *value)
{
JXmlAttr attr;
JLink link;
/* does the attribute exist? */
JI_LIST_FOR_EACH(elem->attributes, link) {
attr = (JXmlAttr)link->data;
if (strcmp(name, jxmlattr_get_name(attr)) == 0) {
jxmlattr_set_value(attr, value);
return;
}
}
attr = jxmlattr_new(name, value);
jlist_append(elem->attributes, attr);
}
JXmlElem jxmlelem_get_elem_by_id(JXmlElem elem, const char *id)
{
const char *elem_id = jxmlelem_get_attr(elem, "id");
JLink link;
/* fprintf(stderr, "- %s id=%s\n", jxmlelem_get_name(elem), elem_id ? elem_id: "(null)"); */
/* this is the element with the specified ID */
if (elem_id && strcmp(elem_id, id) == 0)
return elem;
/* go through the children */
JI_LIST_FOR_EACH(((JXmlNode)elem)->children, link) {
JXmlNode child = (JXmlNode)link->data;
if (child->type == JI_XML_ELEM) {
JXmlElem found = jxmlelem_get_elem_by_id((JXmlElem)child, id);
if (found)
return found;
}
}
return NULL;
}
/**********************************************************************/
/* JXmlText */
/**********************************************************************/
JXmlText jxmltext_new(const char *value)
{
return (JXmlText)jxmlnode_new(JI_XML_TEXT, value);
}
void jxmltext_free(JXmlText text)
{
jxmlnode_free((JXmlNode)text);
}
const char *jxmltext_get_text(JXmlText text)
{
return ((JXmlNode)text)->value;
}
void jxmltext_set_text(JXmlText text, const char *value)
{
if (((JXmlNode)text)->value)
jfree(((JXmlNode)text)->value);
((JXmlNode)text)->value = value ? jstrdup(value): NULL;
}
/**********************************************************************/
/* READ XML TAGS */
/**********************************************************************/
static JList read_nodes(JStream stream)
{
JList root_tags = jlist_new();
JList parent_stack = jlist_new();
char *s, buf[1024];
while (jstream_gets(stream, buf, sizeof(buf))) {
for (s=buf; *s; s++) {
/* beginning of a new element */
if (*s == '<') {
char *tag_start = s + 1;
int open = (s[1] != '/');
bool auto_closed = FALSE;
JXmlElem tag;
/* comment? */
if (s[1] == '!' && s[2] == '-' && s[3] == '-') {
s += 2;
for (;;) {
if (strncmp (s, "-->", 3) == 0) {
s += 2;
break;
}
else if (*s == 0) {
if (!jstream_gets(stream, buf+strlen(buf), sizeof(buf)-strlen(buf)))
break;
s = buf;
}
else {
s++;
}
}
continue;
}
/* go to end of the tag */
for (; ; s++) {
if (*s == '>') {
if (*(s-1) == '/') {
*(s-1) = 0;
auto_closed = TRUE;
}
break;
}
else if (*s == '\"') {
for (s++; ; s++) {
if (*s == 0) {
if (!jstream_gets(stream, buf+strlen(buf), sizeof(buf)-strlen(buf)))
break;
s--;
}
else if ((*s == '\"') && (*(s-1) != '\\')) {
break;
}
}
if (jstream_eof(stream))
break;
}
else if (*s == 0) {
if (!jstream_gets(stream, buf+strlen(buf), sizeof(buf)-strlen(buf)))
break;
s--;
}
}
*s = 0;
/* create the new tag */
tag = read_elem(open ? tag_start: tag_start+1);
if (tag) {
/* fprintf (stderr, "%s tag: %s (parent %s)\n", */
/* open ? "open": "close", */
/* jxmlelem_get_name(tag), */
/* !jlist_empty(parent_stack) ? ((JXmlNode)jlist_first_data(parent_stack))->value: "ROOT"); */
/* open a level */
if (open) {
/* add this tag in parent list */
if (jlist_empty(parent_stack))
jlist_append(root_tags, tag);
else
jxmlnode_add_child(jlist_first(parent_stack)->data, (JXmlNode)tag);
/* if it isn't closed in the same open tag */
if (!auto_closed)
/* add to the parent stack */
jlist_prepend(parent_stack, tag);
}
/* close a level */
else {
if ((!jlist_empty(parent_stack)) &&
(strcmp(((JXmlNode)tag)->value, ((JXmlNode)jlist_first_data(parent_stack))->value) == 0)) {
/* remove the first tag from stack */
jlist_remove(parent_stack, jlist_first_data(parent_stack));
}
else {
/* XXX error msg */
/* printf ("you must open the tag before close it\n"); */
}
jxmlelem_free(tag);
}
}
}
/* put characters in the last JXmlText */
else {
if (!jlist_empty(parent_stack)) {
/* TODO */
/* JXmlNode tag = jlist_first(parent_stack)->data; */
/* if (tag->value || IS_BLANK(*s)) { */
/* int len = tag->value ? strlen(tag->value): 0; */
/* tag->value = jrealloc(tag->value, len+2); */
/* tag->value[len] = *s; */
/* tag->value[len+1] = 0; */
/* } */
}
}
}
}
jlist_free(parent_stack);
return root_tags;
}
static JXmlElem read_elem(char *elem_string)
{
char c, *s;
JXmlElem elem;
/* find the end of the tag-name */
for (s=elem_string; *s && !IS_BLANK(*s); s++)
;
/* create the new tag with the found name */
c = *s;
*s = 0;
elem = jxmlelem_new(elem_string);
*s = c;
/* continue reading attributes */
while (*s) {
/* jump white spaces */
while (*s && IS_BLANK(*s))
s++;
/* isn't end of string? */
if (*s) {
char *name_beg = s;
char *name;
char *value = NULL;
bool translatable = FALSE;
/* read the attribute-name */
while (*s && !IS_BLANK(*s) && *s != '=')
s++;
c = *s;
*s = 0;
name = jstrdup(name_beg);
*s = c;
if (*s == '=') {
char *value_beg = ++s;
bool go_next = FALSE;
/* see for the translation prefix _() */
if (strncmp(s, "_(\"", 3) == 0) {
translatable = TRUE;
s += 2;
}
if (*s == '\"') {
/* jump the double-quote */
value_beg = ++s;
/* read the attribute-value */
while (*s) {
if (*s == '\\') {
memmove(s, s+1, strlen(s)-1);
}
else if (*s == '\"') {
go_next = TRUE;
break;
}
s++;
}
}
else {
/* read the attribute-value */
while (*s && !IS_BLANK(*s))
s++;
}
c = *s;
*s = 0;
value = jstrdup(value_beg);
*s = c;
if (go_next)
s++;
}
/* add the attribute to the tag */
/* TODO translatable */
jxmlelem_set_attr(elem, name, value);
if (name) jfree(name);
if (value) jfree(value);
}
}
return elem;
}