2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2001 Hiroyuki Yamamoto
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 /* if this is defined all attr.names and tag.names are stored
31 #if defined(SPARSE_MEMORY)
32 #include "stringtable.h"
34 static StringTable *xml_string_table;
36 static void xml_string_table_create(void)
38 if (xml_string_table == NULL)
39 xml_string_table = string_table_new();
41 #define XML_STRING_ADD(str) \
42 string_table_insert_string(xml_string_table, (str))
43 #define XML_STRING_FREE(str) \
44 string_table_free_string(xml_string_table, (str))
46 #define XML_STRING_TABLE_CREATE() \
47 xml_string_table_create()
49 #else /* !SPARSE_MEMORY */
51 #define XML_STRING_ADD(str) \
53 #define XML_STRING_FREE(str) \
56 #define XML_STRING_TABLE_CREATE()
58 #endif /* SPARSE_MEMORY */
60 static gint xml_get_parenthesis (XMLFile *file,
64 XMLFile *xml_open_file(const gchar *path)
68 g_return_val_if_fail(path != NULL, NULL);
70 XML_STRING_TABLE_CREATE();
72 newfile = g_new(XMLFile, 1);
74 newfile->fp = fopen(path, "rb");
80 newfile->buf = g_string_new(NULL);
81 newfile->bufp = newfile->buf->str;
84 newfile->tag_stack = NULL;
86 newfile->is_empty_element = FALSE;
91 void xml_close_file(XMLFile *file)
93 g_return_if_fail(file != NULL);
95 if (file->fp) fclose(file->fp);
97 g_string_free(file->buf, TRUE);
101 while (file->tag_stack != NULL)
107 static GNode *xml_build_tree(XMLFile *file, GNode *parent, guint level)
113 while (xml_parse_next_tag(file) == 0) {
114 if (file->level < level) break;
115 if (file->level == level) {
116 g_warning("xml_build_tree(): Parse error\n");
120 tag = xml_get_current_tag(file);
122 xmlnode = g_new(XMLNode, 1);
123 xmlnode->tag = xml_copy_tag(tag);
124 xmlnode->element = xml_get_element(file);
126 node = g_node_new(xmlnode);
128 node = g_node_append_data(parent, xmlnode);
130 xml_build_tree(file, node, file->level);
131 if (file->level == 0) break;
137 GNode *xml_parse_file(const gchar *path)
142 file = xml_open_file(path);
143 g_return_val_if_fail(file != NULL, NULL);
147 node = xml_build_tree(file, NULL, file->level);
149 xml_close_file(file);
151 #if defined(SPARSE_MEMORY)
152 if (debug_get_mode())
153 string_table_get_stats(xml_string_table);
159 gint xml_get_dtd(XMLFile *file)
161 gchar buf[XMLBUFSIZE];
164 if (xml_get_parenthesis(file, buf, sizeof(buf)) < 0) return -1;
166 if ((*bufp++ == '?') &&
167 (bufp = strcasestr(bufp, "xml")) &&
168 (bufp = strcasestr(bufp + 3, "version")) &&
169 (bufp = strchr(bufp + 7, '?')))
170 file->dtd = g_strdup(buf);
172 g_warning("Can't get xml dtd\n");
179 gint xml_parse_next_tag(XMLFile *file)
181 gchar buf[XMLBUFSIZE];
186 if (file->is_empty_element == TRUE) {
187 file->is_empty_element = FALSE;
192 if (xml_get_parenthesis(file, buf, sizeof(buf)) < 0) {
193 g_warning("xml_parse_next_tag(): Can't parse next tag\n");
199 if (strcmp(xml_get_current_tag(file)->tag, buf + 1) != 0) {
200 g_warning("xml_parse_next_tag(): Tag name mismatch: %s\n", buf);
207 tag = g_new0(XMLTag, 1);
208 xml_push_tag(file, tag);
211 if (len > 0 && buf[len - 1] == '/') {
212 file->is_empty_element = TRUE;
216 if (strlen(buf) == 0) {
217 g_warning("xml_parse_next_tag(): Tag name is empty\n");
221 while (*bufp != '\0' && !isspace(*bufp)) bufp++;
223 tag->tag = XML_STRING_ADD(buf);
227 tag->tag = XML_STRING_ADD(buf);
230 /* parse attributes ( name=value ) */
238 while (isspace(*bufp)) bufp++;
240 if ((p = strchr(attr_name, '=')) == NULL) {
241 g_warning("xml_parse_next_tag(): Syntax error in tag\n");
246 while (isspace(*bufp)) bufp++;
248 if (*bufp != '"' && *bufp != '\'') {
249 g_warning("xml_parse_next_tag(): Syntax error in tag\n");
255 if ((p = strchr(attr_value, quote)) == NULL) {
256 g_warning("xml_parse_next_tag(): Syntax error in tag\n");
262 g_strchomp(attr_name);
263 xml_unescape_str(attr_value);
265 attr = g_new(XMLAttr, 1);
266 attr->name = XML_STRING_ADD(attr_name);
267 attr->value = g_strdup(attr_value);
268 tag->attr = g_list_append(tag->attr, attr);
274 void xml_push_tag(XMLFile *file, XMLTag *tag)
276 g_return_if_fail(tag != NULL);
278 file->tag_stack = g_list_prepend(file->tag_stack, tag);
282 void xml_pop_tag(XMLFile *file)
286 if (!file->tag_stack) return;
288 tag = (XMLTag *)file->tag_stack->data;
291 file->tag_stack = g_list_remove(file->tag_stack, tag);
295 XMLTag *xml_get_current_tag(XMLFile *file)
298 return (XMLTag *)file->tag_stack->data;
303 GList *xml_get_current_tag_attr(XMLFile *file)
307 tag = xml_get_current_tag(file);
308 if (!tag) return NULL;
313 gchar *xml_get_element(XMLFile *file)
318 while ((end = strchr(file->bufp, '<')) == NULL)
319 if (xml_read_line(file) < 0) return NULL;
321 if (end == file->bufp)
324 str = g_strndup(file->bufp, end - file->bufp);
325 /* this is not XML1.0 strict */
327 xml_unescape_str(str);
330 xml_truncate_buf(file);
332 if (str[0] == '\0') {
340 gint xml_read_line(XMLFile *file)
342 gchar buf[XMLBUFSIZE];
345 if (fgets(buf, sizeof(buf), file->fp) == NULL)
348 index = file->bufp - file->buf->str;
350 g_string_append(file->buf, buf);
352 file->bufp = file->buf->str + index;
357 void xml_truncate_buf(XMLFile *file)
361 len = file->bufp - file->buf->str;
363 g_string_erase(file->buf, 0, len);
364 file->bufp = file->buf->str;
368 gboolean xml_compare_tag(XMLFile *file, const gchar *name)
372 tag = xml_get_current_tag(file);
374 if (tag && strcmp(tag->tag, name) == 0)
380 XMLTag *xml_copy_tag(XMLTag *tag)
386 new_tag = g_new(XMLTag, 1);
387 new_tag->tag = XML_STRING_ADD(tag->tag);
388 new_tag->attr = NULL;
389 for (list = tag->attr; list != NULL; list = list->next) {
390 attr = xml_copy_attr((XMLAttr *)list->data);
391 new_tag->attr = g_list_append(new_tag->attr, attr);
397 XMLAttr *xml_copy_attr(XMLAttr *attr)
401 new_attr = g_new(XMLAttr, 1);
402 new_attr->name = XML_STRING_ADD(attr->name);
403 new_attr->value = g_strdup(attr->value);
408 gint xml_unescape_str(gchar *str)
417 while ((start = strchr(p, '&')) != NULL) {
418 if ((end = strchr(start + 1, ';')) == NULL) {
419 g_warning("Unescaped `&' appeared\n");
423 len = end - start + 1;
429 Xstrndup_a(esc_str, start, len, return -1);
430 if (!strcmp(esc_str, "<"))
432 else if (!strcmp(esc_str, ">"))
434 else if (!strcmp(esc_str, "&"))
436 else if (!strcmp(esc_str, "'"))
438 else if (!strcmp(esc_str, """))
446 memmove(start + 1, end + 1, strlen(end + 1) + 1);
453 gint xml_file_put_escape_str(FILE *fp, const gchar *str)
457 g_return_val_if_fail(fp != NULL, -1);
461 for (p = str; *p != '\0'; p++) {
486 void xml_free_node(XMLNode *node)
490 xml_free_tag(node->tag);
491 g_free(node->element);
495 static gboolean xml_free_func(GNode *node, gpointer data)
497 XMLNode *xmlnode = node->data;
499 xml_free_node(xmlnode);
503 void xml_free_tree(GNode *node)
505 g_return_if_fail(node != NULL);
507 g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, xml_free_func,
510 g_node_destroy(node);
513 void xml_free_tag(XMLTag *tag)
517 XML_STRING_FREE(tag->tag);
518 while (tag->attr != NULL) {
519 XMLAttr *attr = (XMLAttr *)tag->attr->data;
520 XML_STRING_FREE(attr->name);
523 tag->attr = g_list_remove(tag->attr, tag->attr->data);
528 static gint xml_get_parenthesis(XMLFile *file, gchar *buf, gint len)
535 while ((start = strchr(file->bufp, '<')) == NULL)
536 if (xml_read_line(file) < 0) return -1;
541 while ((end = strchr(file->bufp, '>')) == NULL)
542 if (xml_read_line(file) < 0) return -1;
544 strncpy2(buf, file->bufp, MIN(end - file->bufp + 1, len));
546 file->bufp = end + 1;
547 xml_truncate_buf(file);
552 void xml_tag_add_attr(XMLTag *tag, const gchar *name, gchar *value)
556 attr = g_new0(XMLAttr, 1);
557 attr->name = XML_STRING_ADD(name);
560 tag->attr = g_list_append(tag->attr, attr);
563 static void xml_write_tree_recursive(GNode *node, FILE *fp)
569 g_return_if_fail(node != NULL);
570 g_return_if_fail(fp != NULL);
572 depth = g_node_depth(node) - 1;
573 for (i = 0; i < depth; i++)
575 tag = ((XMLNode *) node->data)->tag;
577 fprintf(fp, "<%s", tag->tag);
578 for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
579 XMLAttr *attr = (XMLAttr *) cur->data;
581 fprintf(fp, " %s=\"", attr->name);
582 xml_file_put_escape_str(fp, attr->value);
586 if (node->children) {
590 child = node->children;
596 xml_write_tree_recursive(cur, fp);
599 for (i = 0; i < depth; i++)
601 fprintf(fp, "</%s>\n", tag->tag);
606 void xml_write_tree(GNode *node, FILE *fp)
608 xml_write_tree_recursive(node, fp);
611 static gpointer copy_node_func(gpointer nodedata, gpointer data)
613 XMLNode *xmlnode = (XMLNode *) nodedata;
616 newxmlnode = g_new0(XMLNode, 1);
617 newxmlnode->tag = xml_copy_tag(xmlnode->tag);
618 newxmlnode->element = g_strdup(xmlnode->element);
623 GNode *xml_tree_copy(GNode *node)
625 return g_node_map(node, copy_node_func, NULL);