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.
27 #include "../codeconv.h"
30 /* if this is defined all attr.names and tag.names are stored
32 #if defined(SPARSE_MEMORY)
33 #include "stringtable.h"
35 static StringTable *xml_string_table;
37 static void xml_string_table_create(void)
39 if (xml_string_table == NULL)
40 xml_string_table = string_table_new();
42 #define XML_STRING_ADD(str) \
43 string_table_insert_string(xml_string_table, (str))
44 #define XML_STRING_FREE(str) \
45 string_table_free_string(xml_string_table, (str))
47 #define XML_STRING_TABLE_CREATE() \
48 xml_string_table_create()
50 #else /* !SPARSE_MEMORY */
52 #define XML_STRING_ADD(str) \
54 #define XML_STRING_FREE(str) \
57 #define XML_STRING_TABLE_CREATE()
59 #endif /* SPARSE_MEMORY */
61 static gint xml_get_parenthesis (XMLFile *file,
65 XMLFile *xml_open_file(const gchar *path)
69 g_return_val_if_fail(path != NULL, NULL);
71 XML_STRING_TABLE_CREATE();
73 newfile = g_new(XMLFile, 1);
75 newfile->fp = fopen(path, "rb");
81 newfile->buf = g_string_new(NULL);
82 newfile->bufp = newfile->buf->str;
85 newfile->tag_stack = NULL;
87 newfile->is_empty_element = FALSE;
92 void xml_close_file(XMLFile *file)
94 g_return_if_fail(file != NULL);
96 if (file->fp) fclose(file->fp);
98 g_string_free(file->buf, TRUE);
102 while (file->tag_stack != NULL)
108 static GNode *xml_build_tree(XMLFile *file, GNode *parent, guint level)
114 while (xml_parse_next_tag(file) == 0) {
115 if (file->level < level) break;
116 if (file->level == level) {
117 g_warning("xml_build_tree(): Parse error\n");
121 tag = xml_get_current_tag(file);
123 xmlnode = g_new(XMLNode, 1);
124 xmlnode->tag = xml_copy_tag(tag);
125 xmlnode->element = xml_get_element(file);
127 node = g_node_new(xmlnode);
129 node = g_node_append_data(parent, xmlnode);
131 xml_build_tree(file, node, file->level);
132 if (file->level == 0) break;
138 GNode *xml_parse_file(const gchar *path)
143 file = xml_open_file(path);
144 g_return_val_if_fail(file != NULL, NULL);
148 node = xml_build_tree(file, NULL, file->level);
150 xml_close_file(file);
152 #if defined(SPARSE_MEMORY)
153 if (debug_get_mode())
154 string_table_get_stats(xml_string_table);
160 gint xml_get_dtd(XMLFile *file)
162 gchar buf[XMLBUFSIZE];
165 if (xml_get_parenthesis(file, buf, sizeof(buf)) < 0) return -1;
167 if ((*bufp++ == '?') &&
168 (bufp = strcasestr(bufp, "xml")) &&
169 (bufp = strcasestr(bufp + 3, "version")) &&
170 (bufp = strchr(bufp + 7, '?')))
171 file->dtd = g_strdup(buf);
173 g_warning("Can't get xml dtd\n");
180 gint xml_parse_next_tag(XMLFile *file)
182 gchar buf[XMLBUFSIZE];
187 if (file->is_empty_element == TRUE) {
188 file->is_empty_element = FALSE;
193 if (xml_get_parenthesis(file, buf, sizeof(buf)) < 0) {
194 g_warning("xml_parse_next_tag(): Can't parse next tag\n");
200 if (strcmp(xml_get_current_tag(file)->tag, buf + 1) != 0) {
201 g_warning("xml_parse_next_tag(): Tag name mismatch: %s\n", buf);
208 tag = g_new0(XMLTag, 1);
209 xml_push_tag(file, tag);
212 if (len > 0 && buf[len - 1] == '/') {
213 file->is_empty_element = TRUE;
217 if (strlen(buf) == 0) {
218 g_warning("xml_parse_next_tag(): Tag name is empty\n");
222 while (*bufp != '\0' && !isspace(*bufp)) bufp++;
224 tag->tag = XML_STRING_ADD(buf);
228 tag->tag = XML_STRING_ADD(buf);
231 /* parse attributes ( name=value ) */
234 gchar *attr_name, *attr_value;
235 gchar *utf8attr_name, *utf8attr_value;
239 while (isspace(*bufp)) bufp++;
241 if ((p = strchr(attr_name, '=')) == NULL) {
242 g_warning("xml_parse_next_tag(): Syntax error in tag\n");
247 while (isspace(*bufp)) bufp++;
249 if (*bufp != '"' && *bufp != '\'') {
250 g_warning("xml_parse_next_tag(): Syntax error in tag\n");
256 if ((p = strchr(attr_value, quote)) == NULL) {
257 g_warning("xml_parse_next_tag(): Syntax error in tag\n");
263 g_strchomp(attr_name);
264 xml_unescape_str(attr_value);
266 attr = g_new(XMLAttr, 1);
268 utf8attr_name = conv_codeset_strdup
270 conv_get_current_charset_str(),
272 utf8attr_value = conv_codeset_strdup
274 conv_get_current_charset_str(),
276 if (!utf8attr_name) {
277 g_warning("xml_parse_next_tag(): "
278 "faild to convert character set of attr_name\n");
279 utf8attr_name = g_strdup(attr_name);
281 if (!utf8attr_value) {
282 g_warning("xml_parse_next_tag(): "
283 "faild to convert character set of attr_value\n");
284 utf8attr_value = g_strdup(attr_value);
287 attr->name = XML_STRING_ADD(utf8attr_name);
288 attr->value = utf8attr_value;
289 tag->attr = g_list_append(tag->attr, attr);
291 g_free(utf8attr_name);
297 void xml_push_tag(XMLFile *file, XMLTag *tag)
299 g_return_if_fail(tag != NULL);
301 file->tag_stack = g_list_prepend(file->tag_stack, tag);
305 void xml_pop_tag(XMLFile *file)
309 if (!file->tag_stack) return;
311 tag = (XMLTag *)file->tag_stack->data;
314 file->tag_stack = g_list_remove(file->tag_stack, tag);
318 XMLTag *xml_get_current_tag(XMLFile *file)
321 return (XMLTag *)file->tag_stack->data;
326 GList *xml_get_current_tag_attr(XMLFile *file)
330 tag = xml_get_current_tag(file);
331 if (!tag) return NULL;
336 gchar *xml_get_element(XMLFile *file)
342 while ((end = strchr(file->bufp, '<')) == NULL)
343 if (xml_read_line(file) < 0) return NULL;
345 if (end == file->bufp)
348 str = g_strndup(file->bufp, end - file->bufp);
349 /* this is not XML1.0 strict */
351 xml_unescape_str(str);
354 xml_truncate_buf(file);
356 if (str[0] == '\0') {
361 utf8str = conv_codeset_strdup
363 conv_get_current_charset_str(),
366 g_warning("xml_get_element(): "
367 "faild to convert character set.\n");
375 gint xml_read_line(XMLFile *file)
377 gchar buf[XMLBUFSIZE];
380 if (fgets(buf, sizeof(buf), file->fp) == NULL)
383 index = file->bufp - file->buf->str;
385 g_string_append(file->buf, buf);
387 file->bufp = file->buf->str + index;
392 void xml_truncate_buf(XMLFile *file)
396 len = file->bufp - file->buf->str;
398 g_string_erase(file->buf, 0, len);
399 file->bufp = file->buf->str;
403 gboolean xml_compare_tag(XMLFile *file, const gchar *name)
407 tag = xml_get_current_tag(file);
409 if (tag && strcmp(tag->tag, name) == 0)
415 XMLTag *xml_copy_tag(XMLTag *tag)
421 new_tag = g_new(XMLTag, 1);
422 new_tag->tag = XML_STRING_ADD(tag->tag);
423 new_tag->attr = NULL;
424 for (list = tag->attr; list != NULL; list = list->next) {
425 attr = xml_copy_attr((XMLAttr *)list->data);
426 new_tag->attr = g_list_append(new_tag->attr, attr);
432 XMLAttr *xml_copy_attr(XMLAttr *attr)
436 new_attr = g_new(XMLAttr, 1);
437 new_attr->name = XML_STRING_ADD(attr->name);
438 new_attr->value = g_strdup(attr->value);
443 gint xml_unescape_str(gchar *str)
452 while ((start = strchr(p, '&')) != NULL) {
453 if ((end = strchr(start + 1, ';')) == NULL) {
454 g_warning("Unescaped `&' appeared\n");
458 len = end - start + 1;
464 Xstrndup_a(esc_str, start, len, return -1);
465 if (!strcmp(esc_str, "<"))
467 else if (!strcmp(esc_str, ">"))
469 else if (!strcmp(esc_str, "&"))
471 else if (!strcmp(esc_str, "'"))
473 else if (!strcmp(esc_str, """))
481 memmove(start + 1, end + 1, strlen(end + 1) + 1);
489 gint xml_file_put_escape_str(FILE *fp, const gchar *str)
491 const gchar *src_codeset = CS_UTF_8;
492 const gchar *dest_codeset = conv_get_current_charset_str();
493 gchar *tmpstr = NULL;
496 g_return_val_if_fail(fp != NULL, -1);
500 tmpstr = conv_codeset_strdup(str, src_codeset, dest_codeset);
502 g_warning("xml_file_put_escape_str(): Faild to convert character set.");
503 tmpstr = g_strdup(str);
506 for (p = tmpstr; *p != '\0'; p++) {
533 void xml_free_node(XMLNode *node)
537 xml_free_tag(node->tag);
538 g_free(node->element);
542 static gboolean xml_free_func(GNode *node, gpointer data)
544 XMLNode *xmlnode = node->data;
546 xml_free_node(xmlnode);
550 void xml_free_tree(GNode *node)
552 g_return_if_fail(node != NULL);
554 g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, xml_free_func,
557 g_node_destroy(node);
560 void xml_free_tag(XMLTag *tag)
564 XML_STRING_FREE(tag->tag);
565 while (tag->attr != NULL) {
566 XMLAttr *attr = (XMLAttr *)tag->attr->data;
567 XML_STRING_FREE(attr->name);
570 tag->attr = g_list_remove(tag->attr, tag->attr->data);
575 static gint xml_get_parenthesis(XMLFile *file, gchar *buf, gint len)
582 while ((start = strchr(file->bufp, '<')) == NULL)
583 if (xml_read_line(file) < 0) return -1;
588 while ((end = strchr(file->bufp, '>')) == NULL)
589 if (xml_read_line(file) < 0) return -1;
591 strncpy2(buf, file->bufp, MIN(end - file->bufp + 1, len));
593 file->bufp = end + 1;
594 xml_truncate_buf(file);
599 void xml_tag_add_attr(XMLTag *tag, const gchar *name, gchar *value)
603 attr = g_new0(XMLAttr, 1);
604 attr->name = XML_STRING_ADD(name);
607 tag->attr = g_list_append(tag->attr, attr);