axing-xml-parser: Check for incomplete syntax in parse context
[axing:axing.git] / axing-xml-parser.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * Copyright (C) 2012 Shaun McCance  <shaunm@gnome.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library 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 GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Author: Shaun McCance  <shaunm@gnome.org>
21  */
22
23 #include "axing-dtd-schema.h"
24 #include "axing-namespace-map.h"
25 #include "axing-private.h"
26 #include "axing-resource.h"
27 #include "axing-stream.h"
28 #include "axing-xml-parser.h"
29
30 #define NS_XML "http://www.w3.org/XML/1998/namespace"
31
32 typedef enum {
33     PARSER_STATE_NONE,
34
35     PARSER_STATE_START,
36     PARSER_STATE_PROLOG,
37     PARSER_STATE_EPILOG,
38
39     PARSER_STATE_STELM_BASE,
40     PARSER_STATE_STELM_ATTNAME,
41     PARSER_STATE_STELM_ATTEQ,
42     PARSER_STATE_STELM_ATTVAL,
43     PARSER_STATE_ENDELM,
44
45     PARSER_STATE_TEXT,
46     PARSER_STATE_COMMENT,
47     PARSER_STATE_CDATA,
48     PARSER_STATE_INSTRUCTION,
49
50     PARSER_STATE_DOCTYPE,
51     PARSER_STATE_NULL
52 } ParserState;
53
54 typedef enum {
55     DOCTYPE_STATE_START,
56     DOCTYPE_STATE_NAME,
57     DOCTYPE_STATE_SYSTEM,
58     DOCTYPE_STATE_SYSTEM_VAL,
59     DOCTYPE_STATE_PUBLIC,
60     DOCTYPE_STATE_PUBLIC_VAL,
61     DOCTYPE_STATE_EXTID,
62     DOCTYPE_STATE_INT,
63     DOCTYPE_STATE_INT_ELEMENT_START,
64     DOCTYPE_STATE_INT_ELEMENT_NAME,
65     DOCTYPE_STATE_INT_ELEMENT_VALUE,
66     DOCTYPE_STATE_INT_ELEMENT_AFTER,
67     DOCTYPE_STATE_INT_ATTLIST_START,
68     DOCTYPE_STATE_INT_ATTLIST_NAME,
69     DOCTYPE_STATE_INT_ATTLIST_VALUE,
70     DOCTYPE_STATE_INT_ATTLIST_QUOTE,
71     DOCTYPE_STATE_INT_ATTLIST_AFTER,
72     DOCTYPE_STATE_INT_NOTATION_START,
73     DOCTYPE_STATE_INT_NOTATION_NAME,
74     DOCTYPE_STATE_INT_NOTATION_SYSTEM,
75     DOCTYPE_STATE_INT_NOTATION_SYSTEM_VAL,
76     DOCTYPE_STATE_INT_NOTATION_PUBLIC,
77     DOCTYPE_STATE_INT_NOTATION_PUBLIC_VAL,
78     DOCTYPE_STATE_INT_NOTATION_PUBLIC_AFTER,
79     DOCTYPE_STATE_INT_NOTATION_AFTER,
80     DOCTYPE_STATE_INT_ENTITY_START,
81     DOCTYPE_STATE_INT_ENTITY_NAME,
82     DOCTYPE_STATE_INT_ENTITY_VALUE,
83     DOCTYPE_STATE_INT_ENTITY_PUBLIC,
84     DOCTYPE_STATE_INT_ENTITY_PUBLIC_VAL,
85     DOCTYPE_STATE_INT_ENTITY_SYSTEM,
86     DOCTYPE_STATE_INT_ENTITY_SYSTEM_VAL,
87     DOCTYPE_STATE_INT_ENTITY_NDATA,
88     DOCTYPE_STATE_INT_ENTITY_AFTER,
89     DOCTYPE_STATE_AFTER_INT,
90     DOCTYPE_STATE_NULL
91 } DoctypeState;
92
93 typedef enum { XML_1_0, XML_1_1 } XmlVersion;
94
95 typedef struct {
96     char       *qname;
97     GHashTable *nshash;
98 } ParserStackFrame;
99
100 typedef struct {
101     char  *qname;
102     char  *value;
103
104     /* These three may be NULL to avoid extra strdups, but
105        the Stream API defines them never to be NULL when the
106        attribute exists. Value must be computed if NULL. */
107     char  *prefix;
108     char  *localname;
109     char  *namespace;
110
111     int    linenum;
112     int    colnum;
113 } AttributeData;
114
115 static void
116 attribute_data_free (AttributeData *data) {
117     g_free (data->qname); g_free (data->prefix); g_free (data->localname);
118     g_free (data->namespace); g_free (data->value); g_free (data);
119 }
120
121 typedef struct {
122     AxingXmlParser    *parser;
123     GInputStream      *srcstream;
124     GDataInputStream  *datastream;
125     char              *basename;
126     char              *entname;
127     char              *showname;
128
129     ParserState    state;
130     ParserState    init_state;
131     ParserState    prev_state;
132     DoctypeState   doctype_state;
133     int            linenum;
134     int            colnum;
135     int            event_stack_root;
136
137     int            node_linenum;
138     int            node_colnum;
139     int            attr_linenum;
140     int            attr_colnum;
141
142     char          *cur_qname;
143     char          *cur_attrname;
144     char           quotechar;
145     gboolean       empty;
146     GHashTable    *cur_nshash;
147     GHashTable    *cur_attrs;
148     GString       *cur_text;
149     char          *decl_system;
150     char          *decl_public;
151     gboolean       decl_pedef;
152     char          *decl_ndata;
153 } ParserContext;
154
155 struct _AxingXmlParserPrivate {
156     gboolean    async;
157
158     AxingResource      *resource;
159     ParserContext      *context;
160     GCancellable       *cancellable;
161     GSimpleAsyncResult *result;
162     GError             *error;
163
164     XmlVersion          xml_version;
165
166     AxingStreamEventType event_type;
167     char                *event_qname;
168
169     /* These three may be NULL to avoid extra strdups, but
170        the Stream API defines them never to be NULL. Value
171        must be computed if NULL. */
172     char                *event_prefix;
173     char                *event_localname;
174     char                *event_namespace;
175
176     GArray              *event_stack;
177     char                *event_content;
178
179     char               **event_attrkeys;
180     AttributeData      **event_attrvals;
181
182     AxingDtdSchema      *doctype;
183 };
184
185 static void      axing_xml_parser_init          (AxingXmlParser       *parser);
186 static void      axing_xml_parser_class_init    (AxingXmlParserClass  *klass);
187 static void      axing_xml_parser_dispose       (GObject              *object);
188 static void      axing_xml_parser_finalize      (GObject              *object);
189 static void      axing_xml_parser_get_property  (GObject              *object,
190                                                  guint                 prop_id,
191                                                  GValue               *value,
192                                                  GParamSpec           *pspec);
193 static void      axing_xml_parser_set_property  (GObject              *object,
194                                                  guint                 prop_id,
195                                                  const GValue         *value,
196                                                  GParamSpec           *pspec);
197
198 static void      parser_clean_event_data        (AxingXmlParser       *parser);
199
200 static AxingStreamEventType  stream_get_event_type          (AxingStream    *stream);
201 static const char *          stream_get_event_qname         (AxingStream    *stream);
202 static const char *          stream_get_event_prefix        (AxingStream    *stream);
203 static const char *          stream_get_event_localname     (AxingStream    *stream);
204 static const char *          stream_get_event_namespace     (AxingStream    *stream);
205 static const char *          stream_get_event_content       (AxingStream    *stream);
206 const char * const *         stream_get_attributes          (AxingStream    *stream);
207 const char *                 stream_get_attribute_localname (AxingStream    *stream,
208                                                              const char     *qname);
209 const char *                 stream_get_attribute_prefix    (AxingStream    *stream,
210                                                              const char     *qname);
211 const char *                 stream_get_attribute_namespace (AxingStream    *stream,
212                                                              const char     *qname);
213 const char *                 stream_get_attribute_value     (AxingStream    *stream,
214                                                              const char     *name,
215                                                              const char     *ns);
216
217
218 static void         namespace_map_interface_init (AxingNamespaceMapInterface *iface);
219 static const char * namespace_map_get_namespace  (AxingNamespaceMap   *map,
220                                                   const char          *prefix);
221
222 static void      context_resource_read_cb       (AxingResource        *resource,
223                                                  GAsyncResult         *result,
224                                                  ParserContext        *context);
225 static void      context_start_async            (ParserContext        *context);
226 static void      context_start_cb               (GBufferedInputStream *stream,
227                                                  GAsyncResult         *res,
228                                                  ParserContext        *context);
229 static void      context_read_line_cb           (GDataInputStream     *stream,
230                                                  GAsyncResult         *res,
231                                                  ParserContext        *context);
232 static void      context_check_end              (ParserContext        *context);
233 static void      context_parse_xml_decl         (ParserContext        *context);
234 static void      context_parse_line             (ParserContext        *context,
235                                                  char                 *line);
236 static void      context_parse_data             (ParserContext        *context,
237                                                  char                 *line);
238 static void      context_parse_doctype          (ParserContext        *context,
239                                                  char                **line);
240 static void      context_parse_doctype_element  (ParserContext        *context,
241                                                  char                **line);
242 static void      context_parse_doctype_attlist  (ParserContext        *context,
243                                                  char                **line);
244 static void      context_parse_doctype_notation (ParserContext        *context,
245                                                  char                **line);
246 static void      context_parse_doctype_entity   (ParserContext        *context,
247                                                  char                **line);
248 static void      context_parse_parameter        (ParserContext        *context,
249                                                  char                **line);
250 static void      context_parse_cdata            (ParserContext        *context,
251                                                  char                **line);
252 static void      context_parse_comment          (ParserContext        *context,
253                                                  char                **line);
254 static void      context_parse_instruction      (ParserContext        *context,
255                                                  char                **line);
256 static void      context_parse_end_element      (ParserContext        *context,
257                                                  char                **line);
258 static void      context_parse_start_element    (ParserContext        *context,
259                                                  char                **line);
260 static void      context_parse_attrs            (ParserContext        *context,
261                                                  char                **line);
262 static void      context_parse_entity           (ParserContext        *context,
263                                                  char                **line);
264 static void      context_parse_text             (ParserContext        *context,
265                                                  char                **line);
266 static void      context_trigger_start_element  (ParserContext        *context);
267
268 static ParserContext * context_new              (AxingXmlParser       *parser);
269 static void            context_free             (ParserContext        *context);
270
271
272 enum {
273     PROP_0,
274     PROP_RESOURCE,
275     N_PROPS
276 };
277
278 G_DEFINE_TYPE_WITH_CODE (AxingXmlParser, axing_xml_parser, AXING_TYPE_STREAM,
279                          G_IMPLEMENT_INTERFACE (AXING_TYPE_NAMESPACE_MAP,
280                                                 namespace_map_interface_init));
281
282 static void
283 axing_xml_parser_init (AxingXmlParser *parser)
284 {
285     parser->priv = G_TYPE_INSTANCE_GET_PRIVATE (parser, AXING_TYPE_XML_PARSER,
286                                                 AxingXmlParserPrivate);
287
288     parser->priv->event_stack = g_array_new (FALSE, FALSE, sizeof(ParserStackFrame));
289 }
290
291 static void
292 axing_xml_parser_class_init (AxingXmlParserClass *klass)
293 {
294     GObjectClass *object_class = G_OBJECT_CLASS (klass);
295     AxingStreamClass *stream_class = AXING_STREAM_CLASS (klass);
296
297     g_type_class_add_private (klass, sizeof (AxingXmlParserPrivate));
298
299     stream_class->get_event_type = stream_get_event_type;
300     stream_class->get_event_qname = stream_get_event_qname;
301     stream_class->get_event_prefix = stream_get_event_prefix;
302     stream_class->get_event_localname = stream_get_event_localname;
303     stream_class->get_event_namespace = stream_get_event_namespace;
304     stream_class->get_event_content = stream_get_event_content;
305
306     stream_class->get_attributes = stream_get_attributes;
307     stream_class->get_attribute_localname = stream_get_attribute_localname;
308     stream_class->get_attribute_prefix = stream_get_attribute_prefix;
309     stream_class->get_attribute_namespace = stream_get_attribute_namespace;
310     stream_class->get_attribute_value = stream_get_attribute_value;
311
312     object_class->get_property = axing_xml_parser_get_property;
313     object_class->set_property = axing_xml_parser_set_property;
314     object_class->dispose = axing_xml_parser_dispose;
315     object_class->finalize = axing_xml_parser_finalize;
316
317     g_object_class_install_property (object_class, PROP_RESOURCE,
318                                      g_param_spec_object ("resource",
319                                                           N_("resource"),
320                                                           N_("The AxingResource to parse data from"),
321                                                           AXING_TYPE_RESOURCE,
322                                                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
323                                                           G_PARAM_STATIC_STRINGS));
324 #ifdef FIXME
325 transport-encoding
326 declared-encoding
327 #endif
328 }
329
330 static void
331 axing_xml_parser_dispose (GObject *object)
332 {
333     AxingXmlParser *parser = AXING_XML_PARSER (object);
334
335     if (parser->priv->resource) {
336         g_object_unref (parser->priv->resource);
337         parser->priv->resource = NULL;
338     }
339
340     if (parser->priv->context) {
341         context_free (parser->priv->context);
342         parser->priv->context = NULL;
343     }
344
345     if (parser->priv->cancellable) {
346         g_object_unref (parser->priv->cancellable);
347         parser->priv->cancellable = NULL;
348     }
349
350     if (parser->priv->result) {
351         g_object_unref (parser->priv->result);
352         parser->priv->result = NULL;
353     }
354
355     if (parser->priv->doctype) {
356         g_object_unref (parser->priv->doctype);
357         parser->priv->doctype = NULL;
358     }
359
360     G_OBJECT_CLASS (axing_xml_parser_parent_class)->dispose (object);
361 }
362
363 static void
364 axing_xml_parser_finalize (GObject *object)
365 {
366     AxingXmlParser *parser = AXING_XML_PARSER (object);
367
368     g_clear_error (&(parser->priv->error));
369
370     parser_clean_event_data (parser);
371
372     if (parser->priv->event_stack)
373         g_array_free (parser->priv->event_stack, TRUE);
374
375     G_OBJECT_CLASS (axing_xml_parser_parent_class)->finalize (object);
376 }
377
378 static void
379 axing_xml_parser_get_property (GObject    *object,
380                                guint       prop_id,
381                                GValue     *value,
382                                GParamSpec *pspec)
383 {
384     AxingXmlParser *parser = AXING_XML_PARSER (object);
385     switch (prop_id) {
386     case PROP_RESOURCE:
387         g_value_set_object (value, parser->priv->resource);
388         break;
389     default:
390         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
391     }
392 }
393
394 static void
395 axing_xml_parser_set_property (GObject      *object,
396                                guint         prop_id,
397                                const GValue *value,
398                                GParamSpec   *pspec)
399 {
400     AxingXmlParser *parser = AXING_XML_PARSER (object);
401     switch (prop_id) {
402     case PROP_RESOURCE:
403         if (parser->priv->resource)
404             g_object_unref (parser->priv->resource);
405         parser->priv->resource = AXING_RESOURCE (g_value_dup_object (value));
406         break;
407     default:
408         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
409     }
410 }
411
412 GQuark
413 axing_xml_parser_error_quark (void)
414 {
415     return g_quark_from_static_string ("axing-parser-error-quark");
416 }
417
418 AxingXmlParser *
419 axing_xml_parser_new (AxingResource *resource,
420                       AxingResolver *resolver)
421 {
422     return g_object_new (AXING_TYPE_XML_PARSER,
423                          "resource", resource,
424                          NULL);
425 }
426
427 static void
428 namespace_map_interface_init (AxingNamespaceMapInterface *iface)
429 {
430     iface->get_namespace = namespace_map_get_namespace;
431 }
432
433 static const char *
434 namespace_map_get_namespace (AxingNamespaceMap *map,
435                              const char        *prefix)
436 {
437     AxingXmlParser *parser = AXING_XML_PARSER (map);
438     ParserStackFrame frame;
439     int i;
440     if (g_str_equal (prefix, "xml")) {
441         return NS_XML;
442     }
443     for (i = parser->priv->event_stack->len - 1; i >= 0; i--) {
444         frame = g_array_index (parser->priv->event_stack, ParserStackFrame, i);
445         if (frame.nshash != NULL) {
446             const char *ns = g_hash_table_lookup (frame.nshash, prefix);
447             if (ns != NULL) {
448                 return ns;
449             }
450         }
451     }
452     return NULL;
453 }
454
455 static void
456 parser_clean_event_data (AxingXmlParser *parser)
457 {
458     g_free (parser->priv->event_qname);
459     parser->priv->event_qname = NULL;
460     g_free (parser->priv->event_prefix);
461     parser->priv->event_prefix = NULL;
462     g_free (parser->priv->event_localname);
463     parser->priv->event_localname = NULL;
464     g_free (parser->priv->event_namespace);
465     parser->priv->event_namespace = NULL;
466     g_free (parser->priv->event_content);
467     parser->priv->event_content = NULL;
468
469     if (parser->priv->event_attrvals != NULL) {
470         gint i;
471         for (i = 0; parser->priv->event_attrvals[i] != NULL; i++)
472             attribute_data_free (parser->priv->event_attrvals[i]);
473         g_free (parser->priv->event_attrvals);
474         parser->priv->event_attrvals = NULL;
475     }
476     /* strings owned by AttributeData structs */
477     g_free (parser->priv->event_attrkeys);
478     parser->priv->event_attrkeys = NULL;
479
480     parser->priv->event_type = AXING_STREAM_EVENT_NONE;
481 }
482
483 static AxingStreamEventType
484 stream_get_event_type (AxingStream *stream)
485 {
486     return AXING_XML_PARSER (stream)->priv->event_type;
487 }
488
489 static const char *
490 stream_get_event_qname (AxingStream *stream)
491 {
492     AxingXmlParser *parser;
493     g_return_val_if_fail (AXING_IS_XML_PARSER (stream), NULL);
494     parser = (AxingXmlParser *) stream;
495     g_return_val_if_fail (parser->priv->event_type == AXING_STREAM_EVENT_START_ELEMENT ||
496                           parser->priv->event_type == AXING_STREAM_EVENT_END_ELEMENT ||
497                           parser->priv->event_type == AXING_STREAM_EVENT_INSTRUCTION,
498                           NULL);
499     return parser->priv->event_qname;
500 }
501
502 static const char *
503 stream_get_event_content (AxingStream *stream)
504 {
505     AxingXmlParser *parser;
506     g_return_val_if_fail (AXING_IS_XML_PARSER (stream), NULL);
507     parser = (AxingXmlParser *) stream;
508     g_return_val_if_fail (parser->priv->event_type == AXING_STREAM_EVENT_CONTENT ||
509                           parser->priv->event_type == AXING_STREAM_EVENT_COMMENT ||
510                           parser->priv->event_type == AXING_STREAM_EVENT_CDATA   ||
511                           parser->priv->event_type == AXING_STREAM_EVENT_INSTRUCTION,
512                           NULL);
513     g_return_val_if_fail (parser->priv->event_type != AXING_STREAM_EVENT_NONE, NULL);
514     return parser->priv->event_content;
515 }
516
517 static const char *
518 stream_get_event_prefix (AxingStream *stream)
519 {
520     AxingXmlParser *parser;
521     g_return_val_if_fail (AXING_IS_XML_PARSER (stream), NULL);
522     parser = (AxingXmlParser *) stream;
523     g_return_val_if_fail (parser->priv->event_type == AXING_STREAM_EVENT_START_ELEMENT ||
524                           parser->priv->event_type == AXING_STREAM_EVENT_END_ELEMENT ||
525                           parser->priv->event_type == AXING_STREAM_EVENT_INSTRUCTION,
526                           NULL);
527     return parser->priv->event_prefix ? parser->priv->event_prefix : "";
528 }
529
530 static const char *
531 stream_get_event_localname (AxingStream *stream)
532 {
533     AxingXmlParser *parser;
534     g_return_val_if_fail (AXING_IS_XML_PARSER (stream), NULL);
535     parser = (AxingXmlParser *) stream;
536     g_return_val_if_fail (parser->priv->event_type == AXING_STREAM_EVENT_START_ELEMENT ||
537                           parser->priv->event_type == AXING_STREAM_EVENT_END_ELEMENT ||
538                           parser->priv->event_type == AXING_STREAM_EVENT_INSTRUCTION,
539                           NULL);
540     return parser->priv->event_localname ? parser->priv->event_localname : parser->priv->event_qname;
541 }
542
543 static const char *
544 stream_get_event_namespace (AxingStream *stream)
545 {
546     AxingXmlParser *parser;
547     g_return_val_if_fail (AXING_IS_XML_PARSER (stream), NULL);
548     parser = (AxingXmlParser *) stream;
549     g_return_val_if_fail (parser->priv->event_type == AXING_STREAM_EVENT_START_ELEMENT ||
550                           parser->priv->event_type == AXING_STREAM_EVENT_END_ELEMENT ||
551                           parser->priv->event_type == AXING_STREAM_EVENT_INSTRUCTION,
552                           NULL);
553     return parser->priv->event_namespace ? parser->priv->event_namespace : "";
554 }
555
556 static char *nokeys[1] = {NULL};
557
558 const char * const *
559 stream_get_attributes (AxingStream *stream)
560 {
561     AxingXmlParser *parser;
562     g_return_val_if_fail (AXING_IS_XML_PARSER (stream), NULL);
563     parser = (AxingXmlParser *) stream;
564     g_return_val_if_fail (parser->priv->event_type == AXING_STREAM_EVENT_START_ELEMENT, NULL);
565     if (parser->priv->event_attrkeys == NULL) {
566         return (const char * const *) nokeys;
567     }
568     return (const char * const *) parser->priv->event_attrkeys;
569 }
570
571 const char *
572 stream_get_attribute_localname (AxingStream *stream,
573                                 const char  *qname)
574 {
575     AxingXmlParser *parser;
576     int i;
577     g_return_val_if_fail (AXING_IS_XML_PARSER (stream), NULL);
578     parser = (AxingXmlParser *) stream;
579     g_return_val_if_fail (parser->priv->event_type == AXING_STREAM_EVENT_START_ELEMENT, NULL);
580     for (i = 0; parser->priv->event_attrvals[i] != NULL; i++) {
581         AttributeData *data = parser->priv->event_attrvals[i];
582         if (g_str_equal (qname, data->qname)) {
583             return data->localname ? data->localname : data->qname;
584         }
585     }
586     return NULL;
587 }
588
589 const char *
590 stream_get_attribute_prefix (AxingStream *stream,
591                              const char  *qname)
592 {
593     AxingXmlParser *parser;
594     int i;
595     g_return_val_if_fail (AXING_IS_XML_PARSER (stream), NULL);
596     parser = (AxingXmlParser *) stream;
597     g_return_val_if_fail (parser->priv->event_type == AXING_STREAM_EVENT_START_ELEMENT, NULL);
598     for (i = 0; parser->priv->event_attrvals[i] != NULL; i++) {
599         AttributeData *data = parser->priv->event_attrvals[i];
600         if (g_str_equal (qname, data->qname)) {
601             return data->prefix ? data->prefix : "";
602         }
603     }
604     return NULL;
605 }
606
607 const char *
608 stream_get_attribute_namespace (AxingStream *stream,
609                                 const char  *qname)
610 {
611     AxingXmlParser *parser;
612     int i;
613     g_return_val_if_fail (AXING_IS_XML_PARSER (stream), NULL);
614     parser = (AxingXmlParser *) stream;
615     g_return_val_if_fail (parser->priv->event_type == AXING_STREAM_EVENT_START_ELEMENT, NULL);
616     for (i = 0; parser->priv->event_attrvals[i] != NULL; i++) {
617         AttributeData *data = parser->priv->event_attrvals[i];
618         if (g_str_equal (qname, data->qname)) {
619             return data->namespace ? data->namespace : "";
620         }
621     }
622     return NULL;
623 }
624
625 const char *
626 stream_get_attribute_value (AxingStream *stream,
627                             const char  *name,
628                             const char  *ns)
629 {
630     AxingXmlParser *parser;
631     int i;
632     g_return_val_if_fail (AXING_IS_XML_PARSER (stream), NULL);
633     parser = (AxingXmlParser *) stream;
634     g_return_val_if_fail (parser->priv->event_type == AXING_STREAM_EVENT_START_ELEMENT, NULL);
635     for (i = 0; parser->priv->event_attrvals[i] != NULL; i++) {
636         AttributeData *data = parser->priv->event_attrvals[i];
637         if (ns == NULL) {
638             if (g_str_equal (name, data->qname))
639                 return data->value;
640         }
641         else {
642             if (g_str_equal (name, data->localname) &&
643                 ((ns[0] == '\0') ? data->namespace == NULL : g_str_equal (ns, data->namespace)))
644                 return data->value;
645         }
646     }
647     return NULL;
648 }
649
650 static void
651 axing_xml_parser_parse_init (AxingXmlParser *parser,
652                              GCancellable   *cancellable)
653 {
654     GFile *file;
655     char *parsename;
656     const char *slash;
657
658     parser->priv->context = context_new (parser);
659     parser->priv->context->state = PARSER_STATE_START;
660
661     if (cancellable)
662         parser->priv->cancellable = g_object_ref (cancellable);
663
664     file = axing_resource_get_file (parser->priv->resource);
665     if (file) {
666         parsename = g_file_get_parse_name (file);
667         slash = strrchr (parsename, '/');
668         if (slash) {
669             parser->priv->context->basename = g_strdup (slash + 1);
670             g_free (parsename);
671         }
672         else {
673             parser->priv->context->basename = parsename;
674         }
675     }
676     else {
677         parser->priv->context->basename = g_strdup ("-");
678     }
679 }
680
681 void
682 axing_xml_parser_parse (AxingXmlParser  *parser,
683                         GCancellable    *cancellable,
684                         GError         **error)
685 {
686     char *line;
687
688     g_return_if_fail (parser->priv->context == NULL);
689
690     axing_xml_parser_parse_init (parser, cancellable);
691
692     parser->priv->async = FALSE;
693
694     parser->priv->context->srcstream = axing_resource_read (parser->priv->resource,
695                                                             cancellable,
696                                                             &(parser->priv->error));
697     if (parser->priv->error)
698         goto error;
699
700     parser->priv->context->datastream = g_data_input_stream_new (parser->priv->context->srcstream);
701     g_buffered_input_stream_fill (G_BUFFERED_INPUT_STREAM (parser->priv->context->datastream),
702                                   1024,
703                                   parser->priv->cancellable,
704                                   &(parser->priv->error));
705     if (parser->priv->error)
706         goto error;
707
708     context_parse_xml_decl (parser->priv->context);
709     if (parser->priv->error)
710         goto error;
711
712     g_data_input_stream_set_newline_type (parser->priv->context->datastream,
713                                           G_DATA_STREAM_NEWLINE_TYPE_ANY);
714     while ((line = g_data_input_stream_read_line (parser->priv->context->datastream, NULL,
715                                                   parser->priv->cancellable,
716                                                   &(parser->priv->error)) )) {
717         if (parser->priv->error)
718             goto error;
719         parser->priv->context->linenum++;
720         parser->priv->context->colnum = 1;
721         context_parse_line (parser->priv->context, line);
722     }
723     if (line == NULL && parser->priv->error == NULL)
724         context_check_end (parser->priv->context);
725
726  error:
727     context_free (parser->priv->context);
728     parser->priv->context = NULL;
729     if (parser->priv->error) {
730         if (error != NULL)
731             *error = parser->priv->error;
732         else
733             g_error_free (parser->priv->error);
734         parser->priv->error = NULL;
735     }
736 }
737
738 void
739 axing_xml_parser_parse_async (AxingXmlParser      *parser,
740                               GCancellable        *cancellable,
741                               GAsyncReadyCallback  callback,
742                               gpointer             user_data)
743 {
744     ParserContext *context;
745     GFile *file;
746     GInputStream *stream;
747
748     g_return_if_fail (parser->priv->context == NULL);
749
750     axing_xml_parser_parse_init (parser, cancellable);
751
752     parser->priv->async = TRUE;
753     parser->priv->result = g_simple_async_result_new (G_OBJECT (parser),
754                                                       callback, user_data,
755                                                       axing_xml_parser_parse_async);
756
757     axing_resource_read_async (parser->priv->resource,
758                                parser->priv->cancellable,
759                                (GAsyncReadyCallback) context_resource_read_cb,
760                                parser->priv->context);
761 }
762
763 void
764 axing_xml_parser_parse_finish (AxingXmlParser *parser,
765                                GAsyncResult   *res,
766                                GError        **error)
767 {
768     g_warn_if_fail (g_simple_async_result_get_source_tag (G_SIMPLE_ASYNC_RESULT (res)) == axing_xml_parser_parse_async);
769
770     if (parser->priv->context) {
771         context_free (parser->priv->context);
772         parser->priv->context = NULL;
773     }
774
775     if (parser->priv->error) {
776         if (error != NULL)
777             *error = parser->priv->error;
778         else
779             g_error_free (parser->priv->error);
780         parser->priv->error = NULL;
781     }
782 }
783
784 #define XML_IS_CHAR(c, context) ((context->parser->priv->xml_version == XML_1_1) ? (c == 0x9 || c == 0xA || c == 0xD || (c >= 0x20 && c <= 0x7E) || c == 0x85 || (c  >= 0xA0 && c <= 0xD7FF) || (c >= 0xE000 && c <= 0xFFFD) || (c >= 0x10000 && c <= 0x10FFFF)) : (c == 0x9 || c == 0xA || c == 0xD || (c >= 0x20 && c <= 0xD7FF) || (c >= 0xE000 && c <= 0xFFFD) || (c >= 0x10000 && c <= 0x10FFFF)))
785
786 #define XML_IS_CHAR_RESTRICTED(c, context) ((context->parser->priv->xml_version == XML_1_1) && ((c >= 0x1 && c <= 0x8) || (c >= 0xB && c <= 0xC) || (c >= 0xE && c <= 0x1F) || (c >= 0x7F && c <= 0x84) || (c >= 0x86 && c <= 0x9F)))
787
788 /* FIXME 1.1 newlines */
789 #define XML_IS_SPACE(c) (c == 0x20 || c == 0x09 || c == 0x0D || c == 0x0A)
790
791 #define XML_IS_NAME_START_CHAR(c) (c == ':' || (c >= 'A' && c <= 'Z') || c == '_' || (c >= 'a' && c <= 'z') || (c >= 0xC0 && c <= 0xD6) || (c >= 0xD8 && c <= 0xF6) || (c >= 0xF8 && c <= 0x2FF) || (c >= 0x370 && c <= 0x37D) || (c >= 0x37F && c <= 0x1FFF) || (c >= 0x200C && c <= 0x200D) || (c >= 0x2070 && c <= 0x218F) || (c >= 0x2C00 && c <= 0x2FEF) || (c >= 0x3001 && c <= 0xD7FF) || (c >= 0xF900 && c <= 0xFDCF) || (c >= 0xFDF0 && c <= 0xFFFD) || (c >= 0x10000 && c <= 0xEFFFF))
792
793 #define XML_IS_NAME_CHAR(c) (XML_IS_NAME_START_CHAR(c) || c == '-' || c == '.' || (c >= '0' && c <= '9') || c == 0xB7 || (c >= 0x300 && c <= 0x36F) || (c >= 0x203F && c <= 0x2040))
794
795 #define XML_GET_NAME(line, namevar, context) { if (XML_IS_NAME_START_CHAR (g_utf8_get_char (*line))) { GString *name = g_string_new (NULL); char *next; gsize bytes; while (XML_IS_NAME_CHAR (g_utf8_get_char (*line))) { next = g_utf8_next_char (*line); bytes = next - *line; g_string_append_len (name, *line, bytes); *line = next; context->colnum += 1; } namevar = g_string_free (name, FALSE); } else { ERROR_SYNTAX(context); } }
796
797 #define ERROR_SYNTAX(context) { context->parser->priv->error = g_error_new(AXING_XML_PARSER_ERROR, AXING_XML_PARSER_ERROR_SYNTAX, "%s:%i:%i:Syntax error.", context->showname ? context->showname : context->basename, context->linenum, context->colnum); goto error; }
798
799 #define ERROR_DUPATTR(context) { context->parser->priv->error = g_error_new(AXING_XML_PARSER_ERROR, AXING_XML_PARSER_ERROR_DUPATTR, "%s:%i:%i:Duplicate attribute \"%s\".", context->showname ? context->showname : context->basename, context->attr_linenum, context->attr_colnum, context->cur_attrname); goto error; }
800
801 #define ERROR_MISSINGEND(context, qname) { context->parser->priv->error = g_error_new (AXING_XML_PARSER_ERROR, AXING_XML_PARSER_ERROR_MISSINGEND, "%s:%i:%i:Missing end tag for \"%s\".", context->showname ? context->showname : context->basename, context->linenum, context->colnum, qname); goto error; }
802
803 #define ERROR_EXTRACONTENT(context) { context->parser->priv->error = g_error_new (AXING_XML_PARSER_ERROR, AXING_XML_PARSER_ERROR_EXTRACONTENT, "%s:%i:%i:Extra content at end of resource.", context->showname ? context->showname : context->basename, context->linenum, context->colnum); goto error; }
804
805 #define ERROR_WRONGEND(context) { context->parser->priv->error = g_error_new(AXING_XML_PARSER_ERROR, AXING_XML_PARSER_ERROR_WRONGEND, "%s:%i:%i:Incorrect end tag \"%s\".", context->showname ? context->showname : context->basename, context->node_linenum, context->node_colnum, context->parser->priv->event_qname); goto error; }
806
807 #define ERROR_ENTITY(context) { context->parser->priv->error = g_error_new(AXING_XML_PARSER_ERROR, AXING_XML_PARSER_ERROR_ENTITY, "%s:%i:%i:Incorrect entity reference.", context->showname ? context->showname : context->basename, context->linenum, context->colnum); goto error; }
808
809 #define ERROR_NS_QNAME(context) { context->parser->priv->error = g_error_new(AXING_XML_PARSER_ERROR, AXING_XML_PARSER_ERROR_NS_QNAME, "%s:%i:%i:Could not parse QName \"%s\".", context->showname ? context->showname : context->basename, context->node_linenum, context->node_colnum, context->parser->priv->event_qname); goto error; }
810
811 #define ERROR_NS_QNAME_ATTR(context, data) { context->parser->priv->error = g_error_new(AXING_XML_PARSER_ERROR, AXING_XML_PARSER_ERROR_NS_QNAME, "%s:%i:%i:Could not parse QName \"%s\".", context->showname ? context->showname : context->basename, data->linenum, data->colnum, data->qname); goto error; }
812
813 #define ERROR_NS_NOTFOUND(context) { context->parser->priv->error = g_error_new(AXING_XML_PARSER_ERROR, AXING_XML_PARSER_ERROR_NS_NOTFOUND, "%s:%i:%i:Could not find namespace for prefix \"%s\".", context->showname ? context->showname : context->basename, context->node_linenum, context->node_colnum, context->parser->priv->event_prefix); goto error; }
814
815 #define ERROR_NS_NOTFOUND_ATTR(context, data) { context->parser->priv->error = g_error_new(AXING_XML_PARSER_ERROR, AXING_XML_PARSER_ERROR_NS_NOTFOUND, "%s:%i:%i:Could not find namespace for prefix \"%s\".", context->showname ? context->showname : context->basename, data->linenum, data->colnum, data->prefix); goto error; }
816
817 #define ERROR_NS_DUPATTR(context, data) { context->parser->priv->error = g_error_new(AXING_XML_PARSER_ERROR, AXING_XML_PARSER_ERROR_NS_DUPATTR, "%s:%i:%i:Duplicate expanded name for attribute \"%s\".", context->showname ? context->showname : context->basename, data->linenum, data->colnum, data->qname); goto error; }
818
819 #define ERROR_NS_INVALID(context, prefix) { context->parser->priv->error = g_error_new(AXING_XML_PARSER_ERROR, AXING_XML_PARSER_ERROR_NS_INVALID, "%s:%i:%i:Invalid namespace for prefix \"%s\".", context->showname ? context->showname : context->basename, context->attr_linenum, context->attr_colnum, prefix); goto error; }
820
821 #define ERROR_FIXME(context) { context->parser->priv->error = g_error_new(AXING_XML_PARSER_ERROR, AXING_XML_PARSER_ERROR_OTHER, "%s:%i:%i:Unsupported feature.", context->showname ? context->showname : context->basename, context->linenum, context->colnum); goto error; }
822
823 /* FIXME: XML 1.1 newlines */
824 #define EAT_SPACES(c, buf, bufsize, context) {gboolean aftercr = FALSE; while((bufsize < 0 || c - buf < bufsize) && XML_IS_SPACE(c[0])) {if (c[0] == 0x0D) { context->colnum = 1; context->linenum++; aftercr = TRUE; } else if (c[0] == 0x0A) { if (!aftercr) { context->colnum = 1; context->linenum++; } aftercr = FALSE; } else { context->colnum++; aftercr = FALSE; } (c)++; }}
825
826 #define CHECK_BUFFER(c, num, buf, bufsize, context) if (c - buf + num > bufsize) { context->parser->priv->error = g_error_new(AXING_XML_PARSER_ERROR, AXING_XML_PARSER_ERROR_BUFFER, "Insufficient buffer for XML declaration\n"); goto error; }
827
828 #define READ_TO_QUOTE(c, buf, bufsize, context, quot) EAT_SPACES (c, buf, bufsize, context); CHECK_BUFFER (c, 1, buf, bufsize, context); if (c[0] != '=') { ERROR_SYNTAX(context); } c += 1; context->colnum += 1; EAT_SPACES (c, buf, bufsize, context); CHECK_BUFFER (c, 1, buf, bufsize, context); if (c[0] != '"' && c[0] != '\'') { ERROR_SYNTAX(context); } quot = c[0]; c += 1; context->colnum += 1;
829
830
831 static void
832 context_resource_read_cb (AxingResource *resource,
833                           GAsyncResult  *result,
834                           ParserContext *context)
835 {
836     context->srcstream = G_INPUT_STREAM (axing_resource_read_finish (resource, result,
837                                                                      &(context->parser->priv->error)));
838     if (context->parser->priv->error) {
839         g_simple_async_result_complete (context->parser->priv->result);
840         return;
841     }
842     context_start_async (context);
843 }
844
845 static void
846 context_start_async (ParserContext *context)
847 {
848     context->datastream = g_data_input_stream_new (context->srcstream);
849
850     g_buffered_input_stream_fill_async (G_BUFFERED_INPUT_STREAM (context->datastream),
851                                         1024,
852                                         G_PRIORITY_DEFAULT,
853                                         context->parser->priv->cancellable,
854                                         (GAsyncReadyCallback) context_start_cb,
855                                         context);
856 }
857
858 static void
859 context_start_cb (GBufferedInputStream *stream,
860                   GAsyncResult         *res,
861                   ParserContext        *context)
862 {
863     g_buffered_input_stream_fill_finish (stream, res, NULL);
864     context_parse_xml_decl (context);
865     if (context->parser->priv->error) {
866         g_simple_async_result_complete (context->parser->priv->result);
867         return;
868     }
869     g_data_input_stream_set_newline_type (context->datastream,
870                                           G_DATA_STREAM_NEWLINE_TYPE_ANY);
871     g_data_input_stream_read_line_async (context->datastream,
872                                          G_PRIORITY_DEFAULT,
873                                          context->parser->priv->cancellable,
874                                          (GAsyncReadyCallback) context_read_line_cb,
875                                          context);
876 }
877
878 static void
879 context_read_line_cb (GDataInputStream *stream,
880                       GAsyncResult     *res,
881                       ParserContext    *context)
882 {
883     gchar *line;
884     if (context->parser->priv->error) {
885         g_simple_async_result_complete (context->parser->priv->result);
886         return;
887     }
888     line = g_data_input_stream_read_line_finish (stream, res, NULL, &(context->parser->priv->error));
889     if (line == NULL && context->parser->priv->error == NULL) {
890         context_check_end (context);
891     }
892     if (line == NULL || context->parser->priv->error) {
893         g_simple_async_result_complete (context->parser->priv->result);
894         return;
895     }
896     context->linenum++;
897     context->colnum = 1;
898     context_parse_line (context, line);
899     g_data_input_stream_read_line_async (context->datastream,
900                                          G_PRIORITY_DEFAULT,
901                                          context->parser->priv->cancellable,
902                                          (GAsyncReadyCallback) context_read_line_cb,
903                                          context);
904 }
905
906 static void
907 context_check_end (ParserContext *context)
908 {
909     if (context->parser->priv->event_stack->len != context->event_stack_root) {
910         ParserStackFrame frame = g_array_index (context->parser->priv->event_stack,
911                                                 ParserStackFrame,
912                                                 context->parser->priv->event_stack->len - 1);
913         ERROR_MISSINGEND(context, frame.qname);
914     }
915     if (context->state != context->init_state &&
916         !(context->state == PARSER_STATE_EPILOG && context->init_state == PARSER_STATE_PROLOG)) {
917         ERROR_SYNTAX(context);
918     }
919  error:
920     return;
921 }
922
923 static void
924 context_parse_xml_decl (ParserContext *context)
925 {
926     int i;
927     gsize bufsize;
928     char *buf, *c;
929     char quot;
930     char *encoding = NULL;
931     buf = (char *) g_buffered_input_stream_peek_buffer (G_BUFFERED_INPUT_STREAM (context->datastream), &bufsize);
932
933     g_return_if_fail (context->state == PARSER_STATE_START);
934
935     if (!(bufsize >= 6 && !strncmp(buf, "<?xml", 5) && XML_IS_SPACE(buf[5]) )) {
936         return;
937     }
938     
939     c = buf + 6;
940     context->colnum += 6;
941     EAT_SPACES (c, buf, bufsize, context);
942
943     CHECK_BUFFER (c, 8, buf, bufsize, context);
944     if (!(!strncmp(c, "version", 7) && (c[7] == '=' || XML_IS_SPACE(c[7])) )) {
945         ERROR_SYNTAX(context);
946     }
947     c += 7; context->colnum += 7;
948
949     READ_TO_QUOTE (c, buf, bufsize, context, quot);
950
951     CHECK_BUFFER (c, 4, buf, bufsize, context);
952     if (c[0] == '1' && c[1] == '.' && (c[2] == '0' || c[2] == '1') && c[3] == quot) {
953         context->parser->priv->xml_version = (c[2] == '0') ? XML_1_0 : XML_1_1;
954     }
955     else {
956         ERROR_SYNTAX(context);
957     }
958     c += 4; context->colnum += 4;
959
960     EAT_SPACES (c, buf, bufsize, context);
961     CHECK_BUFFER (c, 1, buf, bufsize, context);
962     if (c[0] == 'e') {
963         GString *enc;
964         CHECK_BUFFER (c, 9, buf, bufsize, context);
965         if (!(!strncmp(c, "encoding", 8) && (c[8] == '=' || XML_IS_SPACE(c[8])) )) {
966             ERROR_SYNTAX(context);
967         }
968         c += 8; context->colnum += 8;
969
970         READ_TO_QUOTE (c, buf, bufsize, context, quot);
971
972         CHECK_BUFFER (c, 1, buf, bufsize, context);
973         if (!((c[0] >= 'A' && c[0] <= 'Z') || (c[0] >= 'a' && c[0] <= 'z'))) {
974             ERROR_SYNTAX(context);
975         }
976
977         enc = g_string_sized_new (16);
978         while ((c[0] >= 'A' && c[0] <= 'Z') || (c[0] >= 'a' && c[0] <= 'z') ||
979                (c[0] >= '0' && c[0] <= '9') || c[0] == '-' || c[0] == '.' || c[0] == '_') {
980             CHECK_BUFFER (c, 2, buf, bufsize, context);
981             g_string_append_c (enc, c[0]);
982             c += 1; context->colnum += 1;
983         }
984         if (c[0] != quot) {
985             ERROR_SYNTAX(context);
986         }
987         c += 1; context->colnum += 1;
988
989         encoding = g_string_free (enc, FALSE);
990     }
991
992     EAT_SPACES (c, buf, bufsize, context);
993     CHECK_BUFFER (c, 1, buf, bufsize, context);
994     if (c[0] == 's') {
995         CHECK_BUFFER (c, 11, buf, bufsize, context);
996         if (!(!strncmp(c, "standalone", 10) && (c[10] == '=' || XML_IS_SPACE(c[10])) )) {
997             ERROR_SYNTAX(context);
998         }
999         c += 10; context->colnum += 10;
1000
1001         READ_TO_QUOTE (c, buf, bufsize, context, quot);
1002
1003         CHECK_BUFFER (c, 3, buf, bufsize, context);
1004         if (c[0] == 'y' && c[1] == 'e' && c[2] == 's') {
1005             c += 3; context->colnum += 3;
1006         }
1007         else if (c[0] == 'n' && c[1] == 'o') {
1008             c += 2; context->colnum += 2;
1009         }
1010         else {
1011             ERROR_SYNTAX(context);
1012         }
1013         if (c[0] != quot) {
1014             ERROR_SYNTAX(context);
1015         }
1016         c += 1; context->colnum += 1;
1017     }
1018
1019     EAT_SPACES (c, buf, bufsize, context);
1020     CHECK_BUFFER (c, 2, buf, bufsize, context);
1021     if (!(c[0] == '?' && c[1] == '>')) {
1022         ERROR_SYNTAX(context);
1023     }
1024     c += 2; context->colnum += 2;
1025
1026     g_input_stream_skip (G_INPUT_STREAM (context->datastream), c - buf, NULL, NULL);
1027
1028     if (encoding != NULL) {
1029         GConverter *converter;
1030         GInputStream *cstream;
1031         converter = (GConverter *) g_charset_converter_new ("utf-8", encoding, NULL);
1032         if (converter == NULL) {
1033             context->parser->priv->error = g_error_new (AXING_XML_PARSER_ERROR, AXING_XML_PARSER_ERROR_CHARSET,
1034                                                         "Unsupported character set %s\n", encoding);
1035             goto error;
1036         }
1037         cstream = g_converter_input_stream_new (G_INPUT_STREAM (context->datastream), converter);
1038         g_object_unref (converter);
1039         g_object_unref (context->datastream);
1040         context->datastream = g_data_input_stream_new (cstream);
1041         g_object_unref (cstream);
1042         g_free (encoding);
1043     }
1044
1045     context->state = PARSER_STATE_PROLOG;
1046
1047  error:
1048     return;
1049 }
1050
1051 static void
1052 context_parse_line (ParserContext *context, char *line)
1053 {
1054     if (line[0] == '\0' && context->cur_text != NULL) {
1055         g_string_append_c (context->cur_text, 0xA);
1056         return;
1057     }
1058     context_parse_data (context, line);
1059     if (context->state == PARSER_STATE_TEXT) {
1060         if (context->cur_text == NULL)
1061             context->cur_text = g_string_new (NULL);
1062         g_string_append_c (context->cur_text, 0xA);
1063     }
1064 }
1065
1066 static void
1067 context_parse_data (ParserContext *context, char *line)
1068 {
1069     char *c = line;
1070     while (c[0] != '\0') {
1071         switch (context->state) {
1072         case PARSER_STATE_START:
1073         case PARSER_STATE_PROLOG:
1074         case PARSER_STATE_EPILOG:
1075         case PARSER_STATE_TEXT:
1076             if (context->state != PARSER_STATE_TEXT) {
1077                 EAT_SPACES (c, line, -1, context);
1078             }
1079             if (c[0] == '<') {
1080                 if (context->state == PARSER_STATE_TEXT && context->cur_text != NULL) {
1081                     g_free (context->parser->priv->event_content);
1082                     context->parser->priv->event_content = g_string_free (context->cur_text, FALSE);
1083                     context->cur_text = NULL;
1084                     context->parser->priv->event_type = AXING_STREAM_EVENT_CONTENT;
1085
1086                     g_signal_emit_by_name (context->parser, "stream-event");
1087                     parser_clean_event_data (context->parser);
1088                 }
1089                 switch (c[1]) {
1090                 case '!':
1091                     switch (c[2]) {
1092                     case '[':
1093                         context_parse_cdata (context, &c);
1094                         break;
1095                     case 'D':
1096                         context_parse_doctype (context, &c);
1097                         break;
1098                     default:
1099                         context_parse_comment (context, &c);
1100                     }
1101                     break;
1102                 case '?':
1103                     context_parse_instruction (context, &c);
1104                     break;
1105                 case '/':
1106                     context_parse_end_element (context, &c);
1107                     break;
1108                 default:
1109                     if (context->state == PARSER_STATE_EPILOG)
1110                         ERROR_EXTRACONTENT(context);
1111                     context_parse_start_element (context, &c);
1112                 }
1113             }
1114             else if (context->state == PARSER_STATE_EPILOG) {
1115                 ERROR_EXTRACONTENT(context);
1116             }
1117             else if (context->state == PARSER_STATE_TEXT) {
1118                 context_parse_text (context, &c);
1119             }
1120             else if (c[0] != '\0') {
1121                 ERROR_SYNTAX(context);
1122             }
1123             break;
1124         case PARSER_STATE_STELM_BASE:
1125         case PARSER_STATE_STELM_ATTNAME:
1126         case PARSER_STATE_STELM_ATTEQ:
1127         case PARSER_STATE_STELM_ATTVAL:
1128             context_parse_attrs (context, &c);
1129             break;
1130         case PARSER_STATE_ENDELM:
1131             context_parse_end_element (context, &c);
1132             break;
1133         case PARSER_STATE_CDATA:
1134             context_parse_cdata (context, &c);
1135             break;
1136         case PARSER_STATE_COMMENT:
1137             context_parse_comment (context, &c);
1138             break;
1139         case PARSER_STATE_INSTRUCTION:
1140             context_parse_instruction (context, &c);
1141             break;
1142         case PARSER_STATE_DOCTYPE:
1143             context_parse_doctype (context, &c);
1144             break;
1145         default:
1146             g_assert_not_reached ();
1147         }
1148         if (context->parser->priv->error != NULL) {
1149             return;
1150         }
1151     }
1152  error:
1153     return;
1154 }
1155
1156 static void
1157 context_parse_doctype (ParserContext  *context,
1158                        char          **line)
1159 {
1160     switch (context->state) {
1161     case PARSER_STATE_START:
1162     case PARSER_STATE_PROLOG:
1163         if (context->parser->priv->doctype != NULL) {
1164             ERROR_SYNTAX(context);
1165         }
1166         if (!g_str_has_prefix (*line, "<!DOCTYPE")) {
1167             ERROR_SYNTAX(context);
1168         }
1169         (*line) += 9; context->colnum += 9;
1170         EAT_SPACES (*line, *line, -1, context);
1171         context->state = PARSER_STATE_DOCTYPE;
1172         context->doctype_state = DOCTYPE_STATE_START;
1173         if ((*line)[0] == '\0')
1174             return;
1175         break;
1176     case PARSER_STATE_DOCTYPE:
1177         break;
1178     default:
1179         ERROR_SYNTAX(context);
1180     }
1181
1182     if (context->doctype_state == DOCTYPE_STATE_START) {
1183         char *doctype;
1184         EAT_SPACES (*line, *line, -1, context);
1185         if ((*line)[0] == '\0')
1186             return;
1187         XML_GET_NAME(line, doctype, context);
1188         context->parser->priv->doctype = axing_dtd_schema_new ();
1189         axing_dtd_schema_set_doctype (context->parser->priv->doctype, doctype);
1190         g_free (doctype);
1191         context->doctype_state = DOCTYPE_STATE_NAME;
1192     }
1193
1194     if (context->doctype_state == DOCTYPE_STATE_NAME) {
1195         EAT_SPACES (*line, *line, -1, context);
1196         if ((*line)[0] == '\0')
1197             return;
1198         if ((*line)[0] == '>') {
1199             context->doctype_state = DOCTYPE_STATE_NULL;
1200             context->state = PARSER_STATE_PROLOG;
1201             (*line)++; context->colnum++;
1202             return;
1203         }
1204         else if ((*line)[0] == '[') {
1205             (*line)++; context->colnum++;
1206             context->doctype_state = DOCTYPE_STATE_INT;
1207         }
1208         else if (g_str_has_prefix (*line, "SYSTEM")) {
1209             (*line) += 6; context->colnum += 6;
1210             context->doctype_state = DOCTYPE_STATE_SYSTEM;
1211         }
1212         else if (g_str_has_prefix (*line, "PUBLIC")) {
1213             (*line) += 6; context->colnum += 6;
1214             context->doctype_state = DOCTYPE_STATE_PUBLIC;
1215         }
1216         else {
1217             ERROR_SYNTAX(context);
1218         }
1219     }
1220
1221     if (context->doctype_state == DOCTYPE_STATE_PUBLIC) {
1222         EAT_SPACES (*line, *line, -1, context);
1223         if ((*line)[0] == '\0')
1224             return;
1225         if ((*line)[0] == '\'' || (*line)[0] == '"') {
1226             context->quotechar = (*line)[0];
1227             context->cur_text = g_string_new (NULL);
1228             (*line)++; context->colnum++;
1229             context->doctype_state = DOCTYPE_STATE_PUBLIC_VAL;
1230         }
1231         else {
1232             ERROR_SYNTAX(context);
1233         }
1234     }
1235
1236     if (context->doctype_state == DOCTYPE_STATE_PUBLIC_VAL) {
1237         while ((*line)[0] != '\0') {
1238             char c = (*line)[0];
1239             if (c == context->quotechar) {
1240                 char *public = g_string_free (context->cur_text, FALSE);
1241                 context->cur_text = NULL;
1242                 axing_dtd_schema_set_public_id (context->parser->priv->doctype, public);
1243                 g_free (public);
1244                 context->doctype_state = DOCTYPE_STATE_SYSTEM;
1245                 (*line)++; context->colnum++;
1246                 if (!(XML_IS_SPACE((*line)[0]) || (*line)[0] == '\0'))
1247                     ERROR_SYNTAX(context);
1248                 break;
1249             }
1250             if (!(c == 0x20 || c == 0xD || c == 0xA ||
1251                   (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') ||
1252                   c == '!' || c == '#' || c == '$' || c == '%' || (c >= '\'' && c <= '/') ||
1253                   c == ':' || c == ';' || c == '=' || c == '?' || c == '@' || c == '_')) {
1254                 ERROR_SYNTAX(context);
1255             }
1256             g_string_append_c (context->cur_text, c);
1257             (*line)++; context->colnum++;
1258         }
1259         if (context->doctype_state == DOCTYPE_STATE_PUBLIC_VAL && (*line)[0] == '\0') {
1260             g_string_append_c (context->cur_text, 0xA);
1261         }
1262     }
1263
1264     if (context->doctype_state == DOCTYPE_STATE_SYSTEM) {
1265         EAT_SPACES (*line, *line, -1, context);
1266         if ((*line)[0] == '\0')
1267             return;
1268         if ((*line)[0] == '\'' || (*line)[0] == '"') {
1269             context->quotechar = (*line)[0];
1270             context->cur_text = g_string_new (NULL);
1271             (*line)++; context->colnum++;
1272             context->doctype_state = DOCTYPE_STATE_SYSTEM_VAL;
1273         }
1274         else {
1275             ERROR_SYNTAX(context);
1276         }
1277     }
1278
1279     if (context->doctype_state == DOCTYPE_STATE_SYSTEM_VAL) {
1280         while ((*line)[0] != '\0') {
1281             gunichar cp;
1282             char *next;
1283             gsize bytes;
1284             if ((*line)[0] == context->quotechar) {
1285                 char *system = g_string_free (context->cur_text, FALSE);
1286                 context->cur_text = NULL;
1287                 axing_dtd_schema_set_system_id (context->parser->priv->doctype, system);
1288                 g_free (system);
1289                 context->doctype_state = DOCTYPE_STATE_EXTID;
1290                 (*line)++; context->colnum++;
1291                 break;
1292             }
1293             cp = g_utf8_get_char (*line);
1294             if (!XML_IS_CHAR(cp, context)) {
1295                 ERROR_SYNTAX(context);
1296             }
1297             next = g_utf8_next_char (*line);
1298             bytes = next - *line;
1299             g_string_append_len (context->cur_text, *line, bytes);
1300             *line = next; context->colnum += 1;
1301         }
1302         if (context->doctype_state == DOCTYPE_STATE_SYSTEM_VAL && (*line)[0] == '\0') {
1303             g_string_append_c (context->cur_text, 0xA);
1304         }
1305     }
1306
1307     if (context->doctype_state == DOCTYPE_STATE_EXTID) {
1308         EAT_SPACES (*line, *line, -1, context);
1309         if ((*line)[0] == '\0')
1310             return;
1311         switch ((*line)[0]) {
1312         case '\0':
1313             return;
1314         case '[':
1315             (*line)++; context->colnum++;
1316             context->doctype_state = DOCTYPE_STATE_INT;
1317             break;
1318         case '>':
1319             context->doctype_state = DOCTYPE_STATE_NULL;
1320             context->state = PARSER_STATE_PROLOG;
1321             (*line)++; context->colnum++;
1322             return;
1323         default:
1324             ERROR_SYNTAX(context);
1325         }
1326     }
1327
1328     if (context->doctype_state == DOCTYPE_STATE_INT) {
1329         EAT_SPACES (*line, *line, -1, context);
1330         if ((*line)[0] == '\0') {
1331             return;
1332         }
1333         else if ((*line)[0] == ']') {
1334             (*line)++; context->colnum++;
1335             context->doctype_state = DOCTYPE_STATE_AFTER_INT;
1336         }
1337         else if ((*line)[0] == '%') {
1338             context_parse_parameter (context, line);
1339             if (context->parser->priv->error)
1340                 goto error;
1341         }
1342         else if (g_str_has_prefix (*line, "<!ELEMENT")) {
1343             context_parse_doctype_element (context, line);
1344         }
1345         else if (g_str_has_prefix (*line, "<!ATTLIST")) {
1346             context_parse_doctype_attlist (context, line);
1347         }
1348         else if (g_str_has_prefix (*line, "<!ENTITY")) {
1349             context_parse_doctype_entity (context, line);
1350         }
1351         else if (g_str_has_prefix (*line, "<!NOTATION")) {
1352             context_parse_doctype_notation (context, line);
1353         }
1354         else if (g_str_has_prefix (*line, "<!--")) {
1355             context_parse_comment (context, line);
1356         }
1357         else if (g_str_has_prefix (*line, "<?")) {
1358             context_parse_instruction (context, line);
1359         }
1360         else {
1361             ERROR_SYNTAX(context);
1362         }
1363         return;
1364     }
1365
1366     switch (context->doctype_state) {
1367     case DOCTYPE_STATE_INT_ELEMENT_START:
1368     case DOCTYPE_STATE_INT_ELEMENT_NAME:
1369     case DOCTYPE_STATE_INT_ELEMENT_VALUE:
1370     case DOCTYPE_STATE_INT_ELEMENT_AFTER:
1371         context_parse_doctype_element (context, line);
1372         break;
1373     case DOCTYPE_STATE_INT_ATTLIST_START:
1374     case DOCTYPE_STATE_INT_ATTLIST_NAME:
1375     case DOCTYPE_STATE_INT_ATTLIST_VALUE:
1376     case DOCTYPE_STATE_INT_ATTLIST_QUOTE:
1377     case DOCTYPE_STATE_INT_ATTLIST_AFTER:
1378         context_parse_doctype_attlist (context, line);
1379         break;
1380     case DOCTYPE_STATE_INT_NOTATION_START:
1381     case DOCTYPE_STATE_INT_NOTATION_NAME:
1382     case DOCTYPE_STATE_INT_NOTATION_SYSTEM:
1383     case DOCTYPE_STATE_INT_NOTATION_SYSTEM_VAL:
1384     case DOCTYPE_STATE_INT_NOTATION_PUBLIC:
1385     case DOCTYPE_STATE_INT_NOTATION_PUBLIC_VAL:
1386     case DOCTYPE_STATE_INT_NOTATION_PUBLIC_AFTER:
1387     case DOCTYPE_STATE_INT_NOTATION_AFTER:
1388         context_parse_doctype_notation (context, line);
1389         break;
1390     case DOCTYPE_STATE_INT_ENTITY_START:
1391     case DOCTYPE_STATE_INT_ENTITY_NAME:
1392     case DOCTYPE_STATE_INT_ENTITY_VALUE:
1393     case DOCTYPE_STATE_INT_ENTITY_PUBLIC:
1394     case DOCTYPE_STATE_INT_ENTITY_PUBLIC_VAL:
1395     case DOCTYPE_STATE_INT_ENTITY_SYSTEM:
1396     case DOCTYPE_STATE_INT_ENTITY_SYSTEM_VAL:
1397     case DOCTYPE_STATE_INT_ENTITY_NDATA:
1398     case DOCTYPE_STATE_INT_ENTITY_AFTER:
1399         context_parse_doctype_entity (context, line);
1400         break;
1401     default:
1402         break;
1403     }
1404
1405     if (context->doctype_state == DOCTYPE_STATE_AFTER_INT) {
1406         EAT_SPACES (*line, *line, -1, context);
1407         if ((*line)[0] == '\0')
1408             return;
1409         if ((*line)[0] != '>')
1410             ERROR_SYNTAX(context);
1411         context->doctype_state = DOCTYPE_STATE_NULL;
1412         context->state = PARSER_STATE_PROLOG;
1413         (*line)++; context->colnum++;
1414         return;
1415     }
1416
1417  error:
1418     return;
1419 }
1420
1421 static void
1422 context_parse_doctype_element (ParserContext *context, char **line) {
1423     if (context->doctype_state == DOCTYPE_STATE_INT) {
1424         g_assert (g_str_has_prefix(*line, "<!ELEMENT"));
1425         (*line) += 9; context->colnum += 9;
1426         if (!(XML_IS_SPACE((*line)[0]) || (*line)[0] == '\0'))
1427             ERROR_SYNTAX(context);
1428         context->doctype_state = DOCTYPE_STATE_INT_ELEMENT_START;
1429         EAT_SPACES (*line, *line, -1, context);
1430     }
1431
1432     if (context->doctype_state == DOCTYPE_STATE_INT_ELEMENT_START) {
1433         EAT_SPACES (*line, *line, -1, context);
1434         if ((*line)[0] == '\0')
1435             return;
1436         XML_GET_NAME(line, context->cur_qname, context);
1437         context->doctype_state = DOCTYPE_STATE_INT_ELEMENT_NAME;
1438     }
1439
1440     if (context->doctype_state == DOCTYPE_STATE_INT_ELEMENT_NAME) {
1441         EAT_SPACES (*line, *line, -1, context);
1442         if ((*line)[0] == '\0')
1443             return;
1444         if (g_str_has_prefix (*line, "EMPTY")) {
1445             context->cur_text = g_string_new ("EMPTY");
1446             (*line) += 5; context->colnum += 5;
1447             context->doctype_state = DOCTYPE_STATE_INT_ELEMENT_AFTER;
1448         }
1449         else if (g_str_has_prefix (*line, "ANY")) {
1450             context->cur_text = g_string_new ("ANY");
1451             (*line) += 3; context->colnum += 3;
1452             context->doctype_state = DOCTYPE_STATE_INT_ELEMENT_AFTER;
1453         }
1454         else if ((*line)[0] == '(') {
1455             context->cur_text = g_string_new (NULL);
1456             (*line)++; context->colnum++;
1457             g_string_append_c (context->cur_text, '(');
1458             context->doctype_state = DOCTYPE_STATE_INT_ELEMENT_VALUE;
1459         }
1460         else {
1461             ERROR_SYNTAX(context);
1462         }
1463     }
1464
1465     if (context->doctype_state == DOCTYPE_STATE_INT_ELEMENT_VALUE) {
1466         /* not checking the internal syntax here; valid chars only */
1467         while ((*line)[0] != '\0' && (*line)[0] != '>') {
1468             char *next;
1469             gsize bytes;
1470             if (!(XML_IS_NAME_CHAR (g_utf8_get_char (*line)) ||
1471                   XML_IS_SPACE ((*line)[0]) ||
1472                   (*line)[0] == '(' || (*line)[0] == ')' || (*line)[0] == '|' ||
1473                   (*line)[0] == ',' || (*line)[0] == '+' || (*line)[0] == '*' ||
1474                   (*line)[0] == '?' || (*line)[0] == '#' )) {
1475                 ERROR_SYNTAX(context);
1476             }
1477             next = g_utf8_next_char (*line);
1478             bytes = next - *line;
1479             g_string_append_len (context->cur_text, *line, bytes);
1480             *line = next; context->colnum += 1;
1481         }
1482         if ((*line)[0] == '\0')
1483             g_string_append_c (context->cur_text, 0xA);
1484         if ((*line)[0] == '>')
1485             context->doctype_state = DOCTYPE_STATE_INT_ELEMENT_AFTER;
1486     }
1487
1488     if (context->doctype_state == DOCTYPE_STATE_INT_ELEMENT_AFTER) {
1489         char *value;
1490         EAT_SPACES (*line, *line, -1, context);
1491         if ((*line)[0] == '\0')
1492             return;
1493         if ((*line)[0] != '>')
1494             ERROR_SYNTAX(context);
1495         value = g_string_free (context->cur_text, FALSE);
1496         context->cur_text = NULL;
1497         axing_dtd_schema_add_element (context->parser->priv->doctype,
1498                                       context->cur_qname,
1499                                       value,
1500                                       &(context->parser->priv->error));
1501         g_free (value);
1502         g_free (context->cur_qname);
1503         context->cur_qname = NULL;
1504         (*line)++; context->colnum++;
1505         context->doctype_state = DOCTYPE_STATE_INT;
1506     }
1507  error:
1508     return;
1509 }
1510
1511 static void
1512 context_parse_doctype_attlist (ParserContext *context, char **line) {
1513     if (context->doctype_state == DOCTYPE_STATE_INT) {
1514         g_assert (g_str_has_prefix(*line, "<!ATTLIST"));
1515         (*line) += 9; context->colnum += 9;
1516         if (!(XML_IS_SPACE((*line)[0]) || (*line)[0] == '\0'))
1517             ERROR_SYNTAX(context);
1518         context->doctype_state = DOCTYPE_STATE_INT_ATTLIST_START;
1519         EAT_SPACES (*line, *line, -1, context);
1520     }
1521
1522     if (context->doctype_state == DOCTYPE_STATE_INT_ATTLIST_START) {
1523         EAT_SPACES (*line, *line, -1, context);
1524         if ((*line)[0] == '\0')
1525             return;
1526         XML_GET_NAME(line, context->cur_qname, context);
1527         context->doctype_state = DOCTYPE_STATE_INT_ATTLIST_NAME;
1528     }
1529
1530     if (context->doctype_state == DOCTYPE_STATE_INT_ATTLIST_NAME) {
1531         EAT_SPACES (*line, *line, -1, context);
1532         if ((*line)[0] == '\0')
1533             return;
1534         if (XML_IS_NAME_START_CHAR (g_utf8_get_char (*line))) {
1535             context->cur_text = g_string_new (NULL);
1536             context->doctype_state = DOCTYPE_STATE_INT_ATTLIST_VALUE;
1537         }
1538         else {
1539             ERROR_SYNTAX(context);
1540         }
1541     }
1542
1543     if (context->doctype_state == DOCTYPE_STATE_INT_ATTLIST_VALUE) {
1544         /* not checking the internal syntax here; valid chars only */
1545         while ((*line)[0] != '\0' && (*line)[0] != '>') {
1546             char *next;
1547             gsize bytes;
1548             if ((*line)[0] == '"') {
1549                 g_string_append_c (context->cur_text, '"');
1550                 (*line)++; context->colnum++;
1551                 context->doctype_state = DOCTYPE_STATE_INT_ATTLIST_QUOTE;
1552                 break;
1553             }
1554             if (!(XML_IS_NAME_CHAR (g_utf8_get_char (*line)) ||
1555                   XML_IS_SPACE ((*line)[0]) ||
1556                   (*line)[0] == '#' || (*line)[0] == '|' ||
1557                   (*line)[0] == '(' || (*line)[0] == ')' )) {
1558                 ERROR_SYNTAX(context);
1559             }
1560             next = g_utf8_next_char (*line);
1561             bytes = next - *line;
1562             g_string_append_len (context->cur_text, *line, bytes);
1563             *line = next; context->colnum += 1;
1564         }
1565         if ((*line)[0] == '\0')
1566             g_string_append_c (context->cur_text, 0xA);
1567         if ((*line)[0] == '>')
1568             context->doctype_state = DOCTYPE_STATE_INT_ATTLIST_AFTER;
1569     }
1570
1571     if (context->doctype_state == DOCTYPE_STATE_INT_ATTLIST_QUOTE) {
1572         while ((*line)[0] != '\0') {
1573             char *next;
1574             gsize bytes;
1575             if ((*line)[0] == '"') {
1576                 g_string_append_c (context->cur_text, '"');
1577                 (*line)++; context->colnum++;
1578                 context->doctype_state = DOCTYPE_STATE_INT_ATTLIST_VALUE;
1579                 break;
1580             }
1581             if (!XML_IS_CHAR(g_utf8_get_char (*line), context)) {
1582                 ERROR_SYNTAX(context);
1583             }
1584             next = g_utf8_next_char (*line);
1585             bytes = next - *line;
1586             g_string_append_len (context->cur_text, *line, bytes);
1587             *line = next; context->colnum += 1;
1588         }
1589         if ((*line)[0] == '\0')
1590             g_string_append_c (context->cur_text, 0xA);
1591     }
1592
1593     if (context->doctype_state == DOCTYPE_STATE_INT_ATTLIST_AFTER) {
1594         char *value;
1595         EAT_SPACES (*line, *line, -1, context);
1596         if ((*line)[0] == '\0')
1597             return;
1598         if ((*line)[0] != '>')
1599             ERROR_SYNTAX(context);
1600         value = g_string_free (context->cur_text, FALSE);
1601         context->cur_text = NULL;
1602         axing_dtd_schema_add_attlist (context->parser->priv->doctype,
1603                                       context->cur_qname,
1604                                       value,
1605                                       &(context->parser->priv->error));
1606         g_free (value);
1607         g_free (context->cur_qname);
1608         context->cur_qname = NULL;
1609         (*line)++; context->colnum++;
1610         context->doctype_state = DOCTYPE_STATE_INT;
1611     }
1612
1613  error:
1614     return;
1615 }
1616
1617 static void
1618 context_parse_doctype_notation (ParserContext *context, char **line) {
1619     if (context->doctype_state == DOCTYPE_STATE_INT) {
1620         g_assert (g_str_has_prefix(*line, "<!NOTATION"));
1621         (*line) += 10; context->colnum += 10;
1622         if (!(XML_IS_SPACE((*line)[0]) || (*line)[0] == '\0'))
1623             ERROR_SYNTAX(context);
1624         context->doctype_state = DOCTYPE_STATE_INT_NOTATION_START;
1625         EAT_SPACES (*line, *line, -1, context);
1626     }
1627
1628     if (context->doctype_state == DOCTYPE_STATE_INT_NOTATION_START) {
1629         EAT_SPACES (*line, *line, -1, context);
1630         if ((*line)[0] == '\0')
1631             return;
1632         XML_GET_NAME(line, context->cur_qname, context);
1633         context->doctype_state = DOCTYPE_STATE_INT_NOTATION_NAME;
1634     }
1635
1636     if (context->doctype_state == DOCTYPE_STATE_INT_NOTATION_NAME) {
1637         EAT_SPACES (*line, *line, -1, context);
1638         if ((*line)[0] == '\0')
1639             return;
1640         if (g_str_has_prefix (*line, "SYSTEM")) {
1641             (*line) += 6; context->colnum += 6;
1642             context->doctype_state = DOCTYPE_STATE_INT_NOTATION_SYSTEM;
1643         }
1644         else if (g_str_has_prefix (*line, "PUBLIC")) {
1645             (*line) += 6; context->colnum += 6;
1646             context->doctype_state = DOCTYPE_STATE_INT_NOTATION_PUBLIC;
1647         }
1648         else {
1649             ERROR_SYNTAX(context);
1650         }
1651         if (!(XML_IS_SPACE((*line)[0]) || (*line)[0] == '\0'))
1652             ERROR_SYNTAX(context);
1653         EAT_SPACES (*line, *line, -1, context);
1654     }
1655
1656     if (context->doctype_state == DOCTYPE_STATE_INT_NOTATION_SYSTEM) {
1657         EAT_SPACES (*line, *line, -1, context);
1658         if ((*line)[0] == '\0')
1659             return;
1660         if ((*line)[0] == '"' || (*line)[0] == '\'') {
1661             context->quotechar = (*line)[0];
1662             (*line)++; context->colnum++;
1663             context->cur_text = g_string_new (NULL);
1664             context->doctype_state = DOCTYPE_STATE_INT_NOTATION_SYSTEM_VAL;
1665         }
1666         else {
1667             ERROR_SYNTAX(context);
1668         }
1669     }
1670
1671     if (context->doctype_state == DOCTYPE_STATE_INT_NOTATION_SYSTEM_VAL) {
1672         while ((*line)[0] != '\0') {
1673             gunichar cp;
1674             char *next;
1675             gsize bytes;
1676             if ((*line)[0] == context->quotechar) {
1677                 context->decl_system = g_string_free (context->cur_text, FALSE);
1678                 context->cur_text = NULL;
1679                 context->doctype_state = DOCTYPE_STATE_INT_NOTATION_AFTER;
1680                 (*line)++; context->colnum++;
1681                 break;
1682             }
1683             cp = g_utf8_get_char (*line);
1684             if (!XML_IS_CHAR(cp, context)) {
1685                 ERROR_SYNTAX(context);
1686             }
1687             next = g_utf8_next_char (*line);
1688             bytes = next - *line;
1689             g_string_append_len (context->cur_text, *line, bytes);
1690             *line = next; context->colnum += 1;
1691         }
1692         if (context->doctype_state == DOCTYPE_STATE_INT_NOTATION_SYSTEM_VAL && (*line)[0] == '\0') {
1693             g_string_append_c (context->cur_text, 0xA);
1694         }
1695     }
1696
1697     if (context->doctype_state == DOCTYPE_STATE_INT_NOTATION_PUBLIC) {
1698         EAT_SPACES (*line, *line, -1, context);
1699         if ((*line)[0] == '\0')
1700             return;
1701         if ((*line)[0] == '\'' || (*line)[0] == '"') {
1702             context->quotechar = (*line)[0];
1703             context->cur_text = g_string_new (NULL);
1704             (*line)++; context->colnum++;
1705             context->doctype_state = DOCTYPE_STATE_INT_NOTATION_PUBLIC_VAL;
1706         }
1707         else {
1708             ERROR_SYNTAX(context);
1709         }
1710     }
1711
1712     if (context->doctype_state == DOCTYPE_STATE_INT_NOTATION_PUBLIC_VAL) {
1713         while ((*line)[0] != '\0') {
1714             char c = (*line)[0];
1715             if (c == context->quotechar) {
1716                 context->decl_public = g_string_free (context->cur_text, FALSE);
1717                 context->cur_text = NULL;
1718                 context->doctype_state = DOCTYPE_STATE_INT_NOTATION_PUBLIC_AFTER;
1719                 (*line)++; context->colnum++;
1720                 if (!(XML_IS_SPACE((*line)[0]) || (*line)[0] == '\0' || (*line)[0] == '>'))
1721                     ERROR_SYNTAX(context);
1722                 break;
1723             }
1724             if (!(c == 0x20 || c == 0xD || c == 0xA ||
1725                   (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') ||
1726                   c == '!' || c == '#' || c == '$' || c == '%' || (c >= '\'' && c <= '/') ||
1727                   c == ':' || c == ';' || c == '=' || c == '?' || c == '@' || c == '_')) {
1728                 ERROR_SYNTAX(context);
1729             }
1730             g_string_append_c (context->cur_text, c);
1731             (*line)++; context->colnum++;
1732         }
1733         if (context->doctype_state == DOCTYPE_STATE_INT_NOTATION_PUBLIC_VAL && (*line)[0] == '\0') {
1734             g_string_append_c (context->cur_text, 0xA);
1735         }
1736     }
1737
1738     if (context->doctype_state == DOCTYPE_STATE_INT_NOTATION_PUBLIC_AFTER) {
1739         EAT_SPACES (*line, *line, -1, context);
1740         if ((*line)[0] == '\0')
1741             return;
1742         if ((*line)[0] == '\'' || (*line)[0] == '"') {
1743             context->quotechar = (*line)[0];
1744             context->cur_text = g_string_new (NULL);
1745             (*line)++; context->colnum++;
1746             context->doctype_state = DOCTYPE_STATE_INT_NOTATION_SYSTEM_VAL;
1747         }
1748         else if ((*line)[0] == '>') {
1749             context->doctype_state = DOCTYPE_STATE_INT_NOTATION_AFTER;
1750         }
1751         else {
1752             ERROR_SYNTAX(context);
1753         }
1754     }
1755
1756     if (context->doctype_state == DOCTYPE_STATE_INT_NOTATION_AFTER) {
1757         EAT_SPACES (*line, *line, -1, context);
1758         if ((*line)[0] == '\0')
1759             return;
1760         if ((*line)[0] != '>')
1761             ERROR_SYNTAX(context);
1762         axing_dtd_schema_add_notation (context->parser->priv->doctype,
1763                                        context->cur_qname,
1764                                        context->decl_public,
1765                                        context->decl_system);
1766         g_free (context->cur_qname);
1767         context->cur_qname = NULL;
1768         if (context->decl_system) {
1769             g_free (context->decl_system);
1770             context->decl_system = NULL;
1771         }
1772         if (context->decl_public) {
1773             g_free (context->decl_public);
1774             context->decl_public = NULL;
1775         }
1776         (*line)++; context->colnum++;
1777         context->doctype_state = DOCTYPE_STATE_INT;
1778     }
1779
1780  error:
1781     return;
1782 }
1783
1784 static void
1785 context_parse_doctype_entity (ParserContext *context, char **line) {
1786     if (context->doctype_state == DOCTYPE_STATE_INT) {
1787         g_assert (g_str_has_prefix(*line, "<!ENTITY"));
1788         (*line) += 8; context->colnum += 8;
1789         if (!(XML_IS_SPACE((*line)[0]) || (*line)[0] == '\0'))
1790             ERROR_SYNTAX(context);
1791         context->doctype_state = DOCTYPE_STATE_INT_ENTITY_START;
1792         context->decl_pedef = FALSE;
1793         EAT_SPACES (*line, *line, -1, context);
1794     }
1795
1796     if (context->doctype_state == DOCTYPE_STATE_INT_ENTITY_START) {
1797         EAT_SPACES (*line, *line, -1, context);
1798         if ((*line)[0] == '\0')
1799             return;
1800         if ((*line)[0] == '%') {
1801             /* if already true, we've seen a % in this decl */
1802             if (context->decl_pedef)
1803                 ERROR_SYNTAX(context);
1804             context->decl_pedef = TRUE;
1805             (*line)++; context->colnum++;
1806             if (!(XML_IS_SPACE((*line)[0]) || (*line)[0] == '\0'))
1807                 ERROR_SYNTAX(context);
1808             EAT_SPACES (*line, *line, -1, context);
1809             if ((*line)[0] == '\0')
1810                 return;
1811         }
1812         XML_GET_NAME(line, context->cur_qname, context);
1813         context->doctype_state = DOCTYPE_STATE_INT_ENTITY_NAME;
1814     }
1815
1816     if (context->doctype_state == DOCTYPE_STATE_INT_ENTITY_NAME) {
1817         EAT_SPACES (*line, *line, -1, context);
1818         if ((*line)[0] == '\0')
1819             return;
1820         if (g_str_has_prefix (*line, "SYSTEM")) {
1821             (*line) += 6; context->colnum += 6;
1822             if (!(XML_IS_SPACE((*line)[0]) || (*line)[0] == '\0'))
1823                 ERROR_SYNTAX(context);
1824             context->doctype_state = DOCTYPE_STATE_INT_ENTITY_SYSTEM;
1825             EAT_SPACES (*line, *line, -1, context);
1826         }
1827         else if (g_str_has_prefix (*line, "PUBLIC")) {
1828             (*line) += 6; context->colnum += 6;
1829             if (!(XML_IS_SPACE((*line)[0]) || (*line)[0] == '\0'))
1830                 ERROR_SYNTAX(context);
1831             context->doctype_state = DOCTYPE_STATE_INT_ENTITY_PUBLIC;
1832             EAT_SPACES (*line, *line, -1, context);
1833         }
1834         else if ((*line)[0] == '\'' || (*line)[0] == '"') {
1835             context->quotechar = (*line)[0];
1836             context->cur_text = g_string_new (NULL);
1837             (*line)++; context->colnum++;
1838             context->doctype_state = DOCTYPE_STATE_INT_ENTITY_VALUE;
1839         }
1840         else {
1841             ERROR_SYNTAX(context);
1842         }
1843     }
1844
1845     if (context->doctype_state == DOCTYPE_STATE_INT_ENTITY_VALUE) {
1846         while ((*line)[0] != '\0') {
1847             gunichar cp;
1848             char *next;
1849             gsize bytes;
1850             if ((*line)[0] == context->quotechar) {
1851                 /* let the AFTER handler handle cur_text */
1852                 context->doctype_state = DOCTYPE_STATE_INT_ENTITY_AFTER;
1853                 (*line)++; context->colnum++;
1854                 break;
1855             }
1856             cp = g_utf8_get_char (*line);
1857             if (!XML_IS_CHAR(cp, context)) {
1858                 ERROR_SYNTAX(context);
1859             }
1860             next = g_utf8_next_char (*line);
1861             bytes = next - *line;
1862             g_string_append_len (context->cur_text, *line, bytes);
1863             *line = next; context->colnum += 1;
1864         }
1865         if (context->doctype_state == DOCTYPE_STATE_INT_ENTITY_VALUE && (*line)[0] == '\0') {
1866             g_string_append_c (context->cur_text, 0xA);
1867         }
1868     }
1869
1870     if (context->doctype_state == DOCTYPE_STATE_INT_ENTITY_PUBLIC) {
1871         EAT_SPACES (*line, *line, -1, context);
1872         if ((*line)[0] == '\0')
1873             return;
1874         if ((*line)[0] == '\'' || (*line)[0] == '"') {
1875             context->quotechar = (*line)[0];
1876             context->cur_text = g_string_new (NULL);
1877             (*line)++; context->colnum++;
1878             context->doctype_state = DOCTYPE_STATE_INT_ENTITY_PUBLIC_VAL;
1879         }
1880         else {
1881             ERROR_SYNTAX(context);
1882         }
1883     }
1884
1885     if (context->doctype_state == DOCTYPE_STATE_INT_ENTITY_PUBLIC_VAL) {
1886         while ((*line)[0] != '\0') {
1887             char c = (*line)[0];
1888             if (c == context->quotechar) {
1889                 context->decl_public = g_string_free (context->cur_text, FALSE);
1890                 context->cur_text = NULL;
1891                 context->doctype_state = DOCTYPE_STATE_INT_ENTITY_SYSTEM;
1892                 (*line)++; context->colnum++;
1893                 if (!(XML_IS_SPACE((*line)[0]) || (*line)[0] == '\0'))
1894                     ERROR_SYNTAX(context);
1895                 break;
1896             }
1897             if (!(c == 0x20 || c == 0xD || c == 0xA ||
1898                   (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') ||
1899                   c == '!' || c == '#' || c == '$' || c == '%' || (c >= '\'' && c <= '/') ||
1900                   c == ':' || c == ';' || c == '=' || c == '?' || c == '@' || c == '_')) {
1901                 ERROR_SYNTAX(context);
1902             }
1903             g_string_append_c (context->cur_text, c);
1904             (*line)++; context->colnum++;
1905         }
1906         if (context->doctype_state == DOCTYPE_STATE_INT_ENTITY_PUBLIC_VAL && (*line)[0] == '\0') {
1907             g_string_append_c (context->cur_text, 0xA);
1908         }
1909     }
1910
1911     if (context->doctype_state == DOCTYPE_STATE_INT_ENTITY_SYSTEM) {
1912         EAT_SPACES (*line, *line, -1, context);
1913         if ((*line)[0] == '\0')
1914             return;
1915         if ((*line)[0] == '\'' || (*line)[0] == '"') {
1916             context->quotechar = (*line)[0];
1917             context->cur_text = g_string_new (NULL);
1918             (*line)++; context->colnum++;
1919             context->doctype_state = DOCTYPE_STATE_INT_ENTITY_SYSTEM_VAL;
1920         }
1921         else {
1922             ERROR_SYNTAX(context);
1923         }
1924     }
1925
1926     if (context->doctype_state == DOCTYPE_STATE_INT_ENTITY_SYSTEM_VAL) {
1927         while ((*line)[0] != '\0') {
1928             gunichar cp;
1929             char *next;
1930             gsize bytes;
1931             if ((*line)[0] == context->quotechar) {
1932                 context->decl_system = g_string_free (context->cur_text, FALSE);
1933                 context->cur_text = NULL;
1934                 context->doctype_state = DOCTYPE_STATE_INT_ENTITY_AFTER;
1935                 (*line)++; context->colnum++;
1936                 break;
1937             }
1938             cp = g_utf8_get_char (*line);
1939             if (!XML_IS_CHAR(cp, context)) {
1940                 ERROR_SYNTAX(context);
1941             }
1942             next = g_utf8_next_char (*line);
1943             bytes = next - *line;
1944             g_string_append_len (context->cur_text, *line, bytes);
1945             *line = next; context->colnum += 1;
1946         }
1947         if (context->doctype_state == DOCTYPE_STATE_INT_ENTITY_SYSTEM_VAL && (*line)[0] == '\0') {
1948             g_string_append_c (context->cur_text, 0xA);
1949         }
1950     }
1951
1952     if (context->doctype_state == DOCTYPE_STATE_INT_ENTITY_NDATA) {
1953         EAT_SPACES (*line, *line, -1, context);
1954         if ((*line)[0] == '\0')
1955             return;
1956         XML_GET_NAME(line, context->decl_ndata, context);
1957         if (!(XML_IS_SPACE((*line)[0]) || (*line)[0] == '\0' || (*line)[0] == '>'))
1958             ERROR_SYNTAX(context);
1959         context->doctype_state = DOCTYPE_STATE_INT_ENTITY_AFTER;
1960     }
1961
1962     if (context->doctype_state == DOCTYPE_STATE_INT_ENTITY_AFTER) {
1963         EAT_SPACES (*line, *line, -1, context);
1964         if ((*line)[0] == '\0')
1965             return;
1966         if (g_str_has_prefix (*line, "NDATA")) {
1967             if (context->cur_text != NULL    || /* not after an EntityValue */
1968                 context->decl_system == NULL || /* only after PUBLIC or SYSTEM */
1969                 context->decl_pedef == TRUE  || /* no NDATA on param entities */
1970                 context->decl_ndata != NULL  )  /* only one NDATA */
1971                 ERROR_SYNTAX(context);
1972             (*line) += 5; context->colnum += 5;
1973             if (!(XML_IS_SPACE((*line)[0]) || (*line)[0] == '\0'))
1974                 ERROR_SYNTAX(context);
1975             context->doctype_state = DOCTYPE_STATE_INT_ENTITY_NDATA;
1976             return;
1977         }
1978         if ((*line)[0] != '>')
1979             ERROR_SYNTAX(context);
1980         if (context->decl_pedef) {
1981             if (context->cur_text) {
1982                 char *value = g_string_free (context->cur_text, FALSE);
1983                 context->cur_text = NULL;
1984                 axing_dtd_schema_add_parameter (context->parser->priv->doctype,
1985                                                 context->cur_qname,
1986                                                 value);
1987                 g_free (value);
1988             }
1989             else {
1990                 axing_dtd_schema_add_external_parameter (context->parser->priv->doctype,
1991                                                          context->cur_qname,
1992                                                          context->decl_public,
1993                                                          context->decl_system);
1994             }
1995         }
1996         else {
1997             if (context->cur_text) {
1998                 char *value = g_string_free (context->cur_text, FALSE);
1999                 context->cur_text = NULL;
2000                 axing_dtd_schema_add_entity (context->parser->priv->doctype,
2001                                              context->cur_qname,
2002                                              value);
2003                 g_free (value);
2004             }
2005             else if (context->decl_ndata) {
2006                 axing_dtd_schema_add_unparsed_entity (context->parser->priv->doctype,
2007                                                       context->cur_qname,
2008                                                       context->decl_public,
2009                                                       context->decl_system,
2010                                                       context->decl_ndata);
2011             }
2012             else {
2013                 axing_dtd_schema_add_external_entity (context->parser->priv->doctype,
2014                                                       context->cur_qname,
2015                                                       context->decl_public,
2016                                                       context->decl_system);
2017             }
2018         }
2019         g_free (context->cur_qname);
2020         context->cur_qname = NULL;
2021         if (context->cur_text) {
2022             g_string_free (context->cur_text, TRUE);
2023             context->cur_text = NULL;
2024         }
2025         if (context->decl_system) {
2026             g_free (context->decl_system);
2027             context->decl_system = NULL;
2028         }
2029         if (context->decl_public) {
2030             g_free (context->decl_public);
2031             context->decl_public = NULL;
2032         }
2033         if (context->decl_ndata) {
2034             g_free (context->decl_ndata);
2035             context->decl_ndata = NULL;
2036         }
2037         (*line)++; context->colnum++;
2038         context->doctype_state = DOCTYPE_STATE_INT;
2039     }
2040
2041  error:
2042     return;
2043 }
2044
2045 static void
2046 context_parse_parameter (ParserContext  *context,
2047                          char          **line)
2048 {
2049     const char *beg = *line + 1;
2050     char *entname = NULL;
2051     char *value = NULL;
2052     int colnum = context->colnum;
2053     g_assert ((*line)[0] == '%');
2054
2055     (*line)++; colnum++;
2056
2057     while ((*line)[0] != '\0') {
2058         gunichar cp;
2059         if ((*line)[0] == ';') {
2060             break;
2061         }
2062         cp = g_utf8_get_char (*line);
2063         if ((*line) == beg) {
2064             if (!XML_IS_NAME_START_CHAR(cp)) {
2065                 ERROR_ENTITY(context);
2066             }
2067         }
2068         else {
2069             if (!XML_IS_NAME_CHAR(cp)) {
2070                 ERROR_ENTITY(context);
2071             }
2072         }
2073         *line = g_utf8_next_char (*line);
2074         colnum += 1;
2075     }
2076     if ((*line)[0] != ';') {
2077         ERROR_ENTITY(context);
2078     }
2079     entname = g_strndup (beg, *line - beg);
2080     (*line)++; colnum++;
2081
2082     value = axing_dtd_schema_get_parameter (context->parser->priv->doctype, entname);
2083     if (value) {
2084         ParserContext *entctxt = context_new (context->parser);
2085         /* not duping these two, NULL them before free below */
2086         entctxt->basename = context->basename;
2087         entctxt->entname = entname;
2088         entctxt->showname = g_strdup_printf ("%s(%%%s;)", entctxt->basename, entname);
2089         entctxt->state = context->state;
2090         entctxt->init_state = context->state;
2091         entctxt->doctype_state = context->doctype_state;
2092         entctxt->linenum = 1;
2093         entctxt->colnum = 1;
2094         entctxt->event_stack_root = entctxt->parser->priv->event_stack->len;
2095         entctxt->cur_text = context->cur_text;
2096         context->cur_text = NULL;
2097
2098         context_parse_data (entctxt, value);
2099         if (entctxt->parser->priv->error == NULL) {
2100             if (entctxt->state != context->state
2101                 || entctxt->doctype_state != context->doctype_state)
2102                 ERROR_SYNTAX(entctxt);
2103         }
2104
2105         context->state = entctxt->state;
2106         context->cur_text = entctxt->cur_text;
2107         entctxt->cur_text = NULL;
2108         entctxt->basename = NULL;
2109         entctxt->entname = NULL;
2110         context_free (entctxt);
2111         g_free (value);
2112     }
2113     else {
2114         ERROR_FIXME(context);
2115     }
2116
2117  error:
2118     context->colnum = colnum;
2119     g_free (entname);
2120 }
2121
2122 static void
2123 context_parse_cdata (ParserContext *context, char **line)
2124 {
2125     if (context->state != PARSER_STATE_CDATA) {
2126         if (!g_str_has_prefix (*line, "<![CDATA[")) {
2127             ERROR_SYNTAX(context);
2128         }
2129         (*line) += 9; context->colnum += 9;
2130         context->cur_text = g_string_new (NULL);
2131         context->state = PARSER_STATE_CDATA;
2132     }
2133
2134     while ((*line)[0] != '\0') {
2135         gunichar cp;
2136         char *next;
2137         gsize bytes;
2138         if ((*line)[0] == ']' && (*line)[1] == ']' && (*line)[2] == '>') {
2139             (*line) += 3; context->colnum += 3;
2140
2141             context->parser->priv->event_content = g_string_free (context->cur_text, FALSE);
2142             context->cur_text = NULL;
2143             context->parser->priv->event_type = AXING_STREAM_EVENT_CDATA;
2144
2145             g_signal_emit_by_name (context->parser, "stream-event");
2146             parser_clean_event_data (context->parser);
2147             context->state = PARSER_STATE_TEXT;
2148             return;
2149         }
2150         cp = g_utf8_get_char (*line);
2151         if (!XML_IS_CHAR(cp, context)) {
2152             ERROR_SYNTAX(context);
2153         }
2154         next = g_utf8_next_char (*line);
2155         bytes = next - *line;
2156         /* FIXME: newlines */
2157         g_string_append_len (context->cur_text, *line, bytes);
2158         *line = next; context->colnum += 1;
2159     }
2160
2161     if ((*line)[0] == '\0')
2162         g_string_append_c (context->cur_text, 0xA);
2163
2164  error:
2165     return;
2166 }
2167
2168 static void
2169 context_parse_comment (ParserContext *context, char **line)
2170 {
2171     if (context->state != PARSER_STATE_COMMENT) {
2172         if (!g_str_has_prefix (*line, "<!--")) {
2173             ERROR_SYNTAX(context);
2174         }
2175         (*line) += 4; context->colnum += 4;
2176         context->cur_text = g_string_new (NULL);
2177         context->prev_state = context->state;
2178         context->state = PARSER_STATE_COMMENT;
2179     }
2180
2181     while ((*line)[0] != '\0') {
2182         gunichar cp;
2183         char *next;
2184         gsize bytes;
2185         if ((*line)[0] == '-' && (*line)[1] == '-') {
2186             if ((*line)[2] != '>') {
2187                 ERROR_SYNTAX(context);
2188             }
2189             (*line) += 3; context->colnum += 3;
2190
2191             if (context->prev_state == PARSER_STATE_DOCTYPE) {
2192                 /* currently not doing anything with comments in the internal subset */
2193                 g_string_free (context->cur_text, TRUE);
2194                 context->cur_text = NULL;
2195             }
2196             else {
2197                 context->parser->priv->event_content = g_string_free (context->cur_text, FALSE);
2198                 context->cur_text = NULL;
2199                 context->parser->priv->event_type = AXING_STREAM_EVENT_COMMENT;
2200
2201                 g_signal_emit_by_name (context->parser, "stream-event");
2202                 parser_clean_event_data (context->parser);
2203             }
2204             context->state = context->prev_state;
2205             return;
2206         }
2207         cp = g_utf8_get_char (*line);
2208         if (!XML_IS_CHAR(cp, context)) {
2209             ERROR_SYNTAX(context);
2210         }
2211         next = g_utf8_next_char (*line);
2212         bytes = next - *line;
2213         /* FIXME: newlines */
2214         g_string_append_len (context->cur_text, *line, bytes);
2215         *line = next; context->colnum += 1;
2216     }
2217
2218     if ((*line)[0] == '\0')
2219         g_string_append_c (context->cur_text, 0xA);
2220
2221  error:
2222     return;
2223 }
2224
2225 static void
2226 context_parse_instruction (ParserContext *context, char **line)
2227 {
2228     if (context->state != PARSER_STATE_INSTRUCTION) {
2229         if (!g_str_has_prefix (*line, "<?")) {
2230             ERROR_SYNTAX(context);
2231         }
2232         (*line) += 2; context->colnum += 2;
2233         XML_GET_NAME(line, context->parser->priv->event_qname, context);
2234
2235         context->prev_state = context->state;
2236         context->state = PARSER_STATE_INSTRUCTION;
2237     }
2238
2239     if (context->cur_text == NULL) {
2240         EAT_SPACES (*line, *line, -1, context);
2241         if ((*line)[0] == '\0')
2242             return;
2243         context->cur_text = g_string_new (NULL);
2244     }
2245
2246     while ((*line)[0] != '\0') {
2247         gunichar cp;
2248         char *next;
2249         gsize bytes;
2250         if ((*line)[0] == '?' && (*line)[1] == '>') {
2251             (*line) += 2; context->colnum += 2;
2252
2253             if (context->prev_state == PARSER_STATE_DOCTYPE) {
2254                 /* currently not doing anything with PIs in the internal subset */
2255                 g_string_free (context->cur_text, TRUE);
2256                 context->cur_text = NULL;
2257             }
2258             else {
2259                 context->parser->priv->event_content = g_string_free (context->cur_text, FALSE);
2260                 context->cur_text = NULL;
2261                 context->parser->priv->event_type = AXING_STREAM_EVENT_INSTRUCTION;
2262
2263                 g_signal_emit_by_name (context->parser, "stream-event");
2264                 parser_clean_event_data (context->parser);
2265             }
2266             context->state = context->prev_state;
2267             return;
2268         }
2269         cp = g_utf8_get_char (*line);
2270         if (!XML_IS_CHAR(cp, context)) {
2271             ERROR_SYNTAX(context);
2272         }
2273         next = g_utf8_next_char (*line);
2274         bytes = next - *line;
2275         /* FIXME: newlines */
2276         g_string_append_len (context->cur_text, *line, bytes);
2277         *line = next; context->colnum += 1;
2278     }
2279
2280     if ((*line)[0] == '\0')
2281         g_string_append_c (context->cur_text, 0xA);
2282
2283  error:
2284     return;
2285 }
2286
2287 static void
2288 context_parse_end_element (ParserContext *context, char **line)
2289 {
2290     ParserStackFrame frame;
2291     const char *colon;
2292     if (context->state != PARSER_STATE_ENDELM) {
2293         g_assert ((*line)[0] == '<' && (*line)[1] == '/');
2294         context->node_linenum = context->linenum;
2295         context->node_colnum = context->colnum;
2296         (*line) += 2; context->colnum += 2;
2297         XML_GET_NAME(line, context->parser->priv->event_qname, context);
2298     }
2299     EAT_SPACES (*line, *line, -1, context);
2300     if ((*line)[0] == '\0') {
2301         context->state = PARSER_STATE_ENDELM;
2302         return;
2303     }
2304     if ((*line)[0] != '>') {
2305         ERROR_SYNTAX(context);
2306     }
2307     (*line)++; context->colnum++;
2308
2309     colon = strchr (context->parser->priv->event_qname, ':');
2310     if (colon != NULL) {
2311         gunichar cp;
2312         const char *localname;
2313         const char *namespace;
2314         if (colon == context->parser->priv->event_qname) {
2315             ERROR_NS_QNAME(context);
2316         }
2317         localname = colon + 1;
2318         if (localname[0] == '\0' || strchr (localname, ':')) {
2319             ERROR_NS_QNAME(context);
2320         }
2321         cp = g_utf8_get_char (localname);
2322         if (!XML_IS_NAME_START_CHAR(cp)) {
2323             ERROR_NS_QNAME(context);
2324         }
2325         context->parser->priv->event_prefix = g_strndup (context->parser->priv->event_qname,
2326                                                          colon - context->parser->priv->event_qname);
2327         context->parser->priv->event_localname = g_strdup (localname);
2328         namespace = namespace_map_get_namespace (AXING_NAMESPACE_MAP (context->parser),
2329                                                  context->parser->priv->event_prefix);
2330         if (namespace == NULL) {
2331             ERROR_NS_NOTFOUND(context);
2332         }
2333         context->parser->priv->event_namespace = g_strdup (namespace);
2334     }
2335     else {
2336         const char *namespace = namespace_map_get_namespace (AXING_NAMESPACE_MAP (context->parser), "");
2337         if (namespace != NULL)
2338             context->parser->priv->event_namespace = g_strdup (namespace);
2339     }
2340
2341     if (context->parser->priv->event_stack->len <= context->event_stack_root) {
2342         context->linenum = context->node_linenum;
2343         context->colnum = context->node_colnum;
2344         ERROR_EXTRACONTENT(context);
2345     }
2346     frame = g_array_index (context->parser->priv->event_stack,
2347                            ParserStackFrame,
2348                            context->parser->priv->event_stack->len - 1);
2349     g_array_remove_index (context->parser->priv->event_stack,
2350                           context->parser->priv->event_stack->len - 1);
2351     if (!g_str_equal (frame.qname, context->parser->priv->event_qname)) {
2352         ERROR_WRONGEND(context);
2353     }
2354     if (frame.nshash) {
2355         g_hash_table_destroy (frame.nshash);
2356     }
2357     g_free (frame.qname);
2358
2359     context->parser->priv->event_type = AXING_STREAM_EVENT_END_ELEMENT;
2360     g_signal_emit_by_name (context->parser, "stream-event");
2361
2362     if (context->parser->priv->event_stack->len == context->event_stack_root)
2363         context->state = context->init_state;
2364     else
2365         context->state = PARSER_STATE_TEXT;
2366
2367     parser_clean_event_data (context->parser);
2368
2369  error:
2370     return;
2371 }
2372
2373 static void
2374 context_parse_start_element (ParserContext *context, char **line)
2375 {
2376     g_assert ((*line)[0] == '<');
2377     context->node_linenum = context->linenum;
2378     context->node_colnum = context->colnum;
2379     (*line)++; context->colnum++;
2380
2381     g_free (context->cur_qname);
2382     context->cur_qname = NULL;
2383     XML_GET_NAME(line, context->cur_qname, context);
2384
2385     if ((*line)[0] == '>') {
2386         (*line)++; context->colnum++;
2387         context_trigger_start_element (context);
2388     }
2389     else if ((*line)[0] == '/' && (*line)[1] == '>') {
2390         context->empty = TRUE;
2391         (*line) += 2; context->colnum += 2;
2392         context_trigger_start_element (context);
2393     }
2394     else {
2395         context->state = PARSER_STATE_STELM_BASE;
2396     }
2397
2398  error:
2399     return;
2400 }
2401
2402 static void
2403 context_parse_attrs (ParserContext *context, char **line)
2404 {
2405     if (context->state == PARSER_STATE_STELM_BASE) {
2406         EAT_SPACES (*line, *line, -1, context);
2407         if ((*line)[0] == '>') {
2408             (*line)++; context->colnum++;
2409             context_trigger_start_element (context);
2410             return;
2411         }
2412         else if ((*line)[0] == '/' && (*line)[1] == '>') {
2413             context->empty = TRUE;
2414             (*line) += 2; context->colnum += 2;
2415             context_trigger_start_element (context);
2416             return;
2417         }
2418         if ((*line)[0] == '\0') {
2419             return;
2420         }
2421         context->attr_linenum = context->linenum;
2422         context->attr_colnum = context->colnum;
2423
2424         XML_GET_NAME(line, context->cur_attrname, context);
2425         context->state = PARSER_STATE_STELM_ATTNAME;
2426     }
2427     if (context->state == PARSER_STATE_STELM_ATTNAME) {
2428         EAT_SPACES (*line, *line, -1, context);
2429         if ((*line)[0] == '=') {
2430             (*line)++; context->colnum++;
2431             context->state = PARSER_STATE_STELM_ATTEQ;
2432         }
2433         else if ((*line)[0] == '\0') {
2434             return;
2435         }
2436         else {
2437             ERROR_SYNTAX(context);
2438         }
2439     }
2440     if (context->state == PARSER_STATE_STELM_ATTEQ) {
2441         EAT_SPACES (*line, *line, -1, context);
2442         if ((*line)[0] == '\'' || (*line)[0] == '"') {
2443             context->quotechar = (*line)[0];
2444             (*line)++; context->colnum++;
2445             context->cur_text = g_string_new (NULL);
2446             context->state = PARSER_STATE_STELM_ATTVAL;
2447         }
2448         else if ((*line)[0] == '\0') {
2449             return;
2450         }
2451         else {
2452             ERROR_SYNTAX(context);
2453         }
2454     }
2455     if (context->state == PARSER_STATE_STELM_ATTVAL) {
2456         while ((*line)[0] != '\0') {
2457             gunichar cp;
2458             char *next;
2459             gsize bytes;
2460             if ((*line)[0] == context->quotechar) {
2461                 char *xmlns = NULL;
2462                 char *attrval;
2463                 attrval = g_string_free (context->cur_text, FALSE);
2464                 context->cur_text = NULL;
2465
2466                 if (g_str_has_prefix (context->cur_attrname, "xmlns:")) {
2467                     /* FIXME: if cur_attrname == "xmlns:"? */
2468                     xmlns = context->cur_attrname + 6;
2469                 }
2470                 else if (g_str_equal (context->cur_attrname, "xmlns")) {
2471                     xmlns = "";
2472                 }
2473
2474                 if (xmlns != NULL) {
2475                     if (g_str_equal (xmlns, "xml")) {
2476                         if (!g_str_equal(attrval, NS_XML)) {
2477                             ERROR_NS_INVALID(context, xmlns);
2478                         }
2479                     }
2480                     else {
2481                         if (g_str_equal(attrval, NS_XML)) {
2482                             ERROR_NS_INVALID(context, xmlns);
2483                         }
2484                     }
2485                     if (g_str_equal (xmlns, "xmlns")) {
2486                         ERROR_NS_INVALID(context, xmlns);
2487                     }
2488                     if (context->cur_nshash == NULL) {
2489                         context->cur_nshash = g_hash_table_new_full (g_str_hash, g_str_equal,
2490                                                                      g_free, g_free);
2491                     }
2492                     if (g_hash_table_lookup (context->cur_nshash, xmlns) != NULL) {
2493                         ERROR_DUPATTR(context);
2494                     }
2495                     /* FIXME: ensure namespace is valid URI(1.0) or IRI(1.1) */
2496                     g_hash_table_insert (context->cur_nshash,
2497                                          g_strdup (xmlns),
2498                                          attrval);
2499                     g_free (context->cur_attrname);
2500                     context->cur_attrname = NULL;
2501                 }
2502                 else {
2503                     AttributeData *data;
2504                     if (context->cur_attrs == NULL) {
2505                         context->cur_attrs = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
2506                                                                     (GDestroyNotify) attribute_data_free);
2507                     }
2508                     if (g_hash_table_lookup (context->cur_attrs, context->cur_attrname) != NULL) {
2509                         ERROR_DUPATTR(context);
2510                     }
2511                     data = g_new0 (AttributeData, 1);
2512                     data->qname = context->cur_attrname;
2513                     data->value = attrval;
2514                     data->linenum = context->attr_linenum;
2515                     data->colnum = context->attr_colnum;
2516                     g_hash_table_insert (context->cur_attrs,
2517                                          context->cur_attrname,
2518                                          data);
2519                     context->cur_attrname = NULL;
2520                 }
2521
2522                 context->state = PARSER_STATE_STELM_BASE;
2523                 (*line)++; context->colnum++;
2524                 if (!((*line)[0] == '>' || (*line)[0] == '/' ||
2525                       (*line)[0] == '\0' || XML_IS_SPACE ((*line)[0]))) {
2526                     ERROR_SYNTAX(context);
2527                 }
2528                 return;
2529             }
2530             else if ((*line)[0] == '&') {
2531                 context_parse_entity (context, line);
2532                 if (context->parser->priv->error)
2533                     goto error;
2534                 continue;
2535             }
2536             else if ((*line)[0] == '<') {
2537                 ERROR_SYNTAX(context);
2538             }
2539             cp = g_utf8_get_char (*line);
2540             if (!XML_IS_CHAR(cp, context)) {
2541                 ERROR_SYNTAX(context);
2542             }
2543             next = g_utf8_next_char (*line);
2544             bytes = next - *line;
2545             g_string_append_len (context->cur_text, *line, bytes);
2546             *line = next; context->colnum += 1;
2547         }
2548     }
2549  error:
2550     return;
2551 }
2552
2553 static void
2554 context_parse_entity (ParserContext *context, char **line)
2555 {
2556     const char *beg = *line + 1;
2557     char *entname = NULL;
2558     char builtin = '\0';
2559     int colnum = context->colnum;
2560     g_assert ((*line)[0] == '&');
2561
2562     (*line)++; colnum++;
2563
2564     if ((*line)[0] == '#') {
2565         gunichar cp = 0;
2566         (*line)++; colnum++;
2567         if ((*line)[0] == 'x') {
2568             (*line)++; colnum++;
2569             while ((*line)[0] != '\0') {
2570                 if ((*line)[0] == ';')
2571                     break;
2572                 else if ((*line)[0] >= '0' && (*line)[0] <= '9')
2573                     cp = 16 * cp + ((*line)[0] - '0');
2574                 else if ((*line)[0] >= 'A' && (*line)[0] <= 'F')
2575                     cp = 16 * cp + 10 + ((*line)[0] - 'A');
2576                 else if ((*line)[0] >= 'a' && (*line)[0] <= 'f')
2577                     cp = 16 * cp + 10 + ((*line)[0] - 'a');
2578                 else
2579                     ERROR_ENTITY(context);
2580                 (*line)++; colnum++;
2581             }
2582             if ((*line)[0] != ';')
2583                 ERROR_ENTITY(context);
2584             (*line)++; colnum++;
2585         }
2586         else {
2587             while ((*line)[0] != '\0') {
2588                 if ((*line)[0] == ';')
2589                     break;
2590                 else if ((*line)[0] >= '0' && (*line)[0] <= '9')
2591                     cp = 10 * cp + ((*line)[0] - '0');
2592                 else
2593                     ERROR_ENTITY(context);
2594                 (*line)++; colnum++;
2595             }
2596             if ((*line)[0] != ';')
2597                 ERROR_ENTITY(context);
2598             (*line)++; colnum++;
2599         }
2600         if (XML_IS_CHAR(cp, context) || XML_IS_CHAR_RESTRICTED(cp, context)) {
2601             if (context->cur_text == NULL)
2602                 context->cur_text = g_string_new (NULL);
2603             g_string_append_unichar (context->cur_text, cp);
2604         }
2605         else {
2606             ERROR_ENTITY(context);
2607         }
2608     }
2609     else {
2610         while ((*line)[0] != '\0') {
2611             gunichar cp;
2612             if ((*line)[0] == ';') {
2613                 break;
2614             }
2615             cp = g_utf8_get_char (*line);
2616             if ((*line) == beg) {
2617                 if (!XML_IS_NAME_START_CHAR(cp)) {
2618                     ERROR_ENTITY(context);
2619                 }
2620             }
2621             else {
2622                 if (!XML_IS_NAME_CHAR(cp)) {
2623                     ERROR_ENTITY(context);
2624                 }
2625             }
2626             *line = g_utf8_next_char (*line);
2627             colnum += 1;
2628         }
2629         if ((*line)[0] != ';') {
2630             ERROR_ENTITY(context);
2631         }
2632         entname = g_strndup (beg, *line - beg);
2633         (*line)++; colnum++;
2634
2635         if (g_str_equal (entname, "lt"))
2636             builtin = '<';
2637         else if (g_str_equal (entname, "gt"))
2638             builtin = '>';
2639         else if (g_str_equal (entname, "amp"))
2640             builtin = '&';
2641         else if (g_str_equal (entname, "apos"))
2642             builtin = '\'';
2643         else if (g_str_equal (entname, "quot"))
2644             builtin = '"';
2645
2646         if (builtin != '\0') {
2647             if (context->cur_text == NULL) 
2648                 context->cur_text = g_string_new (NULL);
2649             g_string_append_c (context->cur_text, builtin);
2650         }
2651         else {
2652             char *value = axing_dtd_schema_get_entity (context->parser->priv->doctype, entname);
2653             if (value) {
2654                 ParserContext *entctxt = context_new (context->parser);
2655                 /* not duping these two, NULL them before free below */
2656                 entctxt->basename = context->basename;
2657                 entctxt->entname = entname;
2658                 entctxt->showname = g_strdup_printf ("%s(&%s;)", entctxt->basename, entname);
2659                 entctxt->state = context->state;
2660                 entctxt->init_state = context->state;
2661                 entctxt->linenum = 1;
2662                 entctxt->colnum = 1;
2663                 entctxt->event_stack_root = entctxt->parser->priv->event_stack->len;
2664                 entctxt->cur_text = context->cur_text;
2665                 context->cur_text = NULL;
2666
2667                 context_parse_data (entctxt, value);
2668                 if (entctxt->parser->priv->error == NULL)
2669                     context_check_end (entctxt);
2670
2671                 context->state = entctxt->state;
2672                 context->cur_text = entctxt->cur_text;
2673                 entctxt->cur_text = NULL;
2674                 entctxt->basename = NULL;
2675                 entctxt->entname = NULL;
2676                 context_free (entctxt);
2677                 g_free (value);
2678             }
2679             else {
2680                 ERROR_FIXME(context);
2681             }
2682         }
2683     }
2684  error:
2685     context->colnum = colnum;
2686     g_free (entname);
2687 }
2688
2689 static void
2690 context_parse_text (ParserContext *context, char **line)
2691 {
2692     gunichar cp;
2693     char *next;
2694     gsize bytes;
2695     while ((*line)[0] != '\0') {
2696         if ((*line)[0] == '<') {
2697             g_free (context->parser->priv->event_content);
2698             context->parser->priv->event_content = g_string_free (context->cur_text, FALSE);
2699             context->cur_text = NULL;
2700             context->parser->priv->event_type = AXING_STREAM_EVENT_CONTENT;
2701
2702             g_signal_emit_by_name (context->parser, "stream-event");
2703             parser_clean_event_data (context->parser);
2704             return;
2705         }
2706         if ((*line)[0] == '&') {
2707             context_parse_entity (context, line);
2708             if (context->parser->priv->error)
2709                 goto error;
2710             continue;
2711         }
2712         if (context->cur_text == NULL) {
2713             context->cur_text = g_string_new (NULL);
2714         }
2715         cp = g_utf8_get_char (*line);
2716         if (!XML_IS_CHAR (cp, context)) {
2717             ERROR_SYNTAX(context);
2718         }
2719         next = g_utf8_next_char (*line);
2720         bytes = next - *line;
2721         /* FIXME: newlines */
2722         g_string_append_len (context->cur_text, *line, bytes);
2723         *line = next; context->colnum += 1;
2724     }
2725  error:
2726     return;
2727 }
2728
2729 static void
2730 context_trigger_start_element (ParserContext *context)
2731 {
2732     ParserStackFrame frame;
2733     const char *colon;
2734
2735     g_free (context->parser->priv->event_qname);
2736     context->parser->priv->event_qname = context->cur_qname;
2737     context->cur_qname = NULL;
2738
2739     frame.qname = g_strdup (context->parser->priv->event_qname);
2740     frame.nshash = context->cur_nshash;
2741     context->cur_nshash = NULL;
2742     g_array_append_val (context->parser->priv->event_stack, frame);
2743
2744     colon = strchr (context->parser->priv->event_qname, ':');
2745     if (colon != NULL) {
2746         gunichar cp;
2747         const char *localname;
2748         const char *namespace;
2749         if (colon == context->parser->priv->event_qname) {
2750             ERROR_NS_QNAME(context);
2751         }
2752         localname = colon + 1;
2753         if (localname[0] == '\0' || strchr (localname, ':')) {
2754             ERROR_NS_QNAME(context);
2755         }
2756         cp = g_utf8_get_char (localname);
2757         if (!XML_IS_NAME_START_CHAR(cp)) {
2758             ERROR_NS_QNAME(context);
2759         }
2760         context->parser->priv->event_prefix = g_strndup (context->parser->priv->event_qname,
2761                                                          colon - context->parser->priv->event_qname);
2762         context->parser->priv->event_localname = g_strdup (localname);
2763         namespace = namespace_map_get_namespace (AXING_NAMESPACE_MAP (context->parser),
2764                                                  context->parser->priv->event_prefix);
2765         if (namespace == NULL) {
2766             ERROR_NS_NOTFOUND(context);
2767         }
2768         context->parser->priv->event_namespace = g_strdup (namespace);
2769     }
2770     else {
2771         const char *namespace = namespace_map_get_namespace (AXING_NAMESPACE_MAP (context->parser), "");
2772         if (namespace != NULL)
2773             context->parser->priv->event_namespace = g_strdup (namespace);
2774     }
2775
2776     if (context->cur_attrs) {
2777         GHashTableIter attrs;
2778         gpointer key, val;
2779         guint num = g_hash_table_size (context->cur_attrs);
2780         guint cur;
2781
2782         context->parser->priv->event_attrkeys = g_new0 (char *, num + 1);
2783         context->parser->priv->event_attrvals = g_new0 (AttributeData *, num + 1);
2784
2785         cur = 0;
2786         /* We drop from the hash at each iteration, stealing the key and
2787            value. So we re-init the iterator each time. */
2788         while (g_hash_table_iter_init (&attrs, context->cur_attrs),
2789                g_hash_table_iter_next (&attrs, &key, &val)) {
2790             char *qname = (char *) key;
2791             AttributeData *data = (AttributeData *) val;
2792             const char *colon;
2793
2794             /* FIXME: check for duplicate expanded name */
2795
2796             colon = strchr (qname, ':');
2797             if (colon != NULL) {
2798                 gunichar cp;
2799                 const char *localname;
2800                 const char *namespace;
2801                 int pre;
2802                 if (colon == qname) {
2803                     ERROR_NS_QNAME_ATTR(context, data);
2804                 }
2805                 localname = colon + 1;
2806                 if (localname[0] == '\0' || strchr (localname, ':')) {
2807                     ERROR_NS_QNAME_ATTR(context, data);
2808                 }
2809                 cp = g_utf8_get_char (localname);
2810                 if (!XML_IS_NAME_START_CHAR(cp)) {
2811                     ERROR_NS_QNAME_ATTR(context, data);
2812                 }
2813                 data->prefix = g_strndup (qname, colon - qname);
2814                 data->localname = g_strdup (localname);
2815                 namespace = namespace_map_get_namespace (AXING_NAMESPACE_MAP (context->parser),
2816                                                          data->prefix);
2817                 if (namespace == NULL) {
2818                     ERROR_NS_NOTFOUND_ATTR(context, data);
2819                 }
2820                 data->namespace = g_strdup (namespace);
2821                 for (pre = 0; pre < cur; pre++) {
2822                     if (context->parser->priv->event_attrvals[pre]->namespace &&
2823                         g_str_equal (context->parser->priv->event_attrvals[pre]->namespace,
2824                                      data->namespace) &&
2825                         g_str_equal (context->parser->priv->event_attrvals[pre]->localname,