2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999,2000 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.
28 static void xml_free_tag (XMLTag
*tag
);
29 static gint
xml_get_parenthesis (XMLFile
*file
,
33 XMLFile
*xml_open_file(const gchar
*path
)
37 g_return_val_if_fail(path
!= NULL
, NULL
);
39 newfile
= g_new(XMLFile
, 1);
41 newfile
->fp
= fopen(path
, "r");
47 newfile
->buf
= g_string_new(NULL
);
48 newfile
->bufp
= newfile
->buf
->str
;
51 newfile
->tag_stack
= NULL
;
53 newfile
->is_empty_element
= FALSE
;
58 void xml_close_file(XMLFile
*file
)
60 g_return_if_fail(file
!= NULL
);
62 if (file
->fp
) fclose(file
->fp
);
64 g_string_free(file
->buf
, TRUE
);
68 while (file
->tag_stack
!= NULL
)
74 static GNode
*xml_build_tree(XMLFile
*file
, GNode
*parent
, guint level
)
80 while (xml_parse_next_tag(file
) == 0) {
81 if (file
->level
< level
) break;
82 if (file
->level
== level
) {
83 g_warning("xml_build_tree(): Parse error\n");
87 tag
= xml_get_current_tag(file
);
89 xmlnode
= g_new(XMLNode
, 1);
90 xmlnode
->tag
= xml_copy_tag(tag
);
91 xmlnode
->element
= xml_get_element(file
);
93 node
= g_node_new(xmlnode
);
95 node
= g_node_append_data(parent
, xmlnode
);
97 xml_build_tree(file
, node
, file
->level
);
98 if (file
->level
== 0) break;
104 GNode
*xml_parse_file(const gchar
*path
)
109 file
= xml_open_file(path
);
110 g_return_val_if_fail(file
!= NULL
, NULL
);
114 node
= xml_build_tree(file
, NULL
, file
->level
);
116 xml_close_file(file
);
120 gint
xml_get_dtd(XMLFile
*file
)
122 gchar buf
[XMLBUFSIZE
];
125 if (xml_get_parenthesis(file
, buf
, sizeof(buf
)) < 0) return -1;
127 if ((*bufp
++ == '?') &&
128 (bufp
= strcasestr(bufp
, "xml")) &&
129 (bufp
= strcasestr(bufp
+ 3, "version")) &&
130 (bufp
= strchr(bufp
+ 7, '?')))
131 file
->dtd
= g_strdup(buf
);
133 g_warning("Can't get xml dtd\n");
140 gint
xml_parse_next_tag(XMLFile
*file
)
142 gchar buf
[XMLBUFSIZE
];
147 if (file
->is_empty_element
== TRUE
) {
148 file
->is_empty_element
= FALSE
;
153 if (xml_get_parenthesis(file
, buf
, sizeof(buf
)) < 0) {
154 g_warning("xml_parse_next_tag(): Can't parse next tag\n");
160 if (strcmp(xml_get_current_tag(file
)->tag
, buf
+ 1) != 0) {
161 g_warning("xml_parse_next_tag(): Tag name mismatch: %s\n", buf
);
168 tag
= g_new0(XMLTag
, 1);
169 xml_push_tag(file
, tag
);
172 if (len
> 0 && buf
[len
- 1] == '/') {
173 file
->is_empty_element
= TRUE
;
177 if (strlen(buf
) == 0) {
178 g_warning("xml_parse_next_tag(): Tag name is empty\n");
182 while (*bufp
!= '\0' && !isspace(*bufp
)) bufp
++;
184 tag
->tag
= g_strdup(buf
);
188 tag
->tag
= g_strdup(buf
);
191 /* parse attributes ( name=value ) */
199 while (isspace(*bufp
)) bufp
++;
201 if ((p
= strchr(attr_name
, '=')) == NULL
) {
202 g_warning("xml_parse_next_tag(): Syntax error in tag\n");
207 while (isspace(*bufp
)) bufp
++;
209 if (*bufp
!= '"' && *bufp
!= '\'') {
210 g_warning("xml_parse_next_tag(): Syntax error in tag\n");
216 if ((p
= strchr(attr_value
, quote
)) == NULL
) {
217 g_warning("xml_parse_next_tag(): Syntax error in tag\n");
223 g_strchomp(attr_name
);
224 xml_unescape_str(attr_value
);
226 attr
= g_new(XMLAttr
, 1);
227 attr
->name
= g_strdup(attr_name
);
228 attr
->value
= g_strdup(attr_value
);
229 tag
->attr
= g_list_append(tag
->attr
, attr
);
235 void xml_push_tag(XMLFile
*file
, XMLTag
*tag
)
237 g_return_if_fail(tag
!= NULL
);
239 file
->tag_stack
= g_list_prepend(file
->tag_stack
, tag
);
243 void xml_pop_tag(XMLFile
*file
)
247 if (!file
->tag_stack
) return;
249 tag
= (XMLTag
*)file
->tag_stack
->data
;
252 file
->tag_stack
= g_list_remove(file
->tag_stack
, tag
);
256 XMLTag
*xml_get_current_tag(XMLFile
*file
)
259 return (XMLTag
*)file
->tag_stack
->data
;
264 GList
*xml_get_current_tag_attr(XMLFile
*file
)
268 tag
= xml_get_current_tag(file
);
269 if (!tag
) return NULL
;
274 gchar
*xml_get_element(XMLFile
*file
)
279 while ((end
= strchr(file
->bufp
, '<')) == NULL
)
280 if (xml_read_line(file
) < 0) return NULL
;
282 if (end
== file
->bufp
)
285 str
= g_strndup(file
->bufp
, end
- file
->bufp
);
286 /* this is not XML1.0 strict */
288 xml_unescape_str(str
);
291 xml_truncate_buf(file
);
293 if (str
[0] == '\0') {
301 gint
xml_read_line(XMLFile
*file
)
303 gchar buf
[XMLBUFSIZE
];
306 if (fgets(buf
, sizeof(buf
), file
->fp
) == NULL
)
309 index
= file
->bufp
- file
->buf
->str
;
311 g_string_append(file
->buf
, buf
);
313 file
->bufp
= file
->buf
->str
+ index
;
318 void xml_truncate_buf(XMLFile
*file
)
322 len
= file
->bufp
- file
->buf
->str
;
324 g_string_erase(file
->buf
, 0, len
);
325 file
->bufp
= file
->buf
->str
;
329 gboolean
xml_compare_tag(XMLFile
*file
, const gchar
*name
)
333 tag
= xml_get_current_tag(file
);
335 if (tag
&& strcmp(tag
->tag
, name
) == 0)
341 XMLTag
*xml_copy_tag(XMLTag
*tag
)
347 new_tag
= g_new(XMLTag
, 1);
348 new_tag
->tag
= g_strdup(tag
->tag
);
349 new_tag
->attr
= NULL
;
350 for (list
= tag
->attr
; list
!= NULL
; list
= list
->next
) {
351 attr
= xml_copy_attr((XMLAttr
*)list
->data
);
352 new_tag
->attr
= g_list_append(new_tag
->attr
, attr
);
358 XMLAttr
*xml_copy_attr(XMLAttr
*attr
)
362 new_attr
= g_new(XMLAttr
, 1);
363 new_attr
->name
= g_strdup(attr
->name
);
364 new_attr
->value
= g_strdup(attr
->value
);
369 gint
xml_unescape_str(gchar
*str
)
378 while ((start
= strchr(p
, '&')) != NULL
) {
379 if ((end
= strchr(start
+ 1, ';')) == NULL
) {
380 g_warning("Unescaped `&' appeared\n");
384 len
= end
- start
+ 1;
390 Xstrndup_a(esc_str
, start
, len
, return -1);
391 if (!strcmp(esc_str
, "<"))
393 else if (!strcmp(esc_str
, ">"))
395 else if (!strcmp(esc_str
, "&"))
397 else if (!strcmp(esc_str
, "'"))
399 else if (!strcmp(esc_str
, """))
407 memmove(start
+ 1, end
+ 1, strlen(end
+ 1) + 1);
414 gint
xml_file_put_escape_str(FILE *fp
, const gchar
*str
)
418 g_return_val_if_fail(fp
!= NULL
, -1);
422 for (p
= str
; *p
!= '\0'; p
++) {
447 void xml_free_node(XMLNode
*node
)
451 xml_free_tag(node
->tag
);
452 g_free(node
->element
);
456 static gboolean
xml_free_func(GNode
*node
, gpointer data
)
458 XMLNode
*xmlnode
= node
->data
;
460 xml_free_node(xmlnode
);
464 void xml_free_tree(GNode
*node
)
466 g_return_if_fail(node
!= NULL
);
468 g_node_traverse(node
, G_PRE_ORDER
, G_TRAVERSE_ALL
, -1, xml_free_func
,
471 g_node_destroy(node
);
474 static void xml_free_tag(XMLTag
*tag
)
479 while (tag
->attr
!= NULL
) {
480 XMLAttr
*attr
= (XMLAttr
*)tag
->attr
->data
;
484 tag
->attr
= g_list_remove(tag
->attr
, tag
->attr
->data
);
489 static gint
xml_get_parenthesis(XMLFile
*file
, gchar
*buf
, gint len
)
496 while ((start
= strchr(file
->bufp
, '<')) == NULL
)
497 if (xml_read_line(file
) < 0) return -1;
502 while ((end
= strchr(file
->bufp
, '>')) == NULL
)
503 if (xml_read_line(file
) < 0) return -1;
505 strncpy2(buf
, file
->bufp
, MIN(end
- file
->bufp
+ 1, len
));
507 file
->bufp
= end
+ 1;
508 xml_truncate_buf(file
);