]> mj.ucw.cz Git - libucw.git/blob - ucw-xml/ns.c
XML: Bug fixes of namespaces
[libucw.git] / ucw-xml / ns.c
1 /*
2  *      UCW Library -- A simple XML parser -- Namespaces
3  *
4  *      (c) 2015 Martin Mares <mj@ucw.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU Lesser General Public License.
8  */
9
10 #undef LOCAL_DEBUG
11
12 #include <ucw/lib.h>
13 #include <ucw/gary.h>
14 #include <ucw/string.h>
15 #include <ucw-xml/xml.h>
16 #include <ucw-xml/internals.h>
17
18 /*
19  *  This is an implementation of XML namespaces according to
20  *  http://www.w3.org/TR/REC-xml-names/.
21  *
22  *  Currently, we assume that the document does not contain a plethora
23  *  of namespaces and prefixes. So we keep them in memory until the
24  *  document ends.
25  */
26
27 struct ns_hash_entry {
28   uint ns;
29   char name[1];
30 };
31
32 #define HASH_NODE struct ns_hash_entry
33 #define HASH_PREFIX(x) ns_hash_##x
34 #define HASH_KEY_ENDSTRING name
35 #define HASH_WANT_FIND
36 #define HASH_WANT_LOOKUP
37 #define HASH_TABLE_DYNAMIC
38 #define HASH_TABLE_ALLOC
39 #define HASH_LOOKUP_DETECT_NEW
40 #define HASH_GIVE_ALLOC
41 XML_HASH_GIVE_ALLOC
42 #include <ucw/hashtable.h>
43
44 struct xml_ns_prefix {
45   struct xml_ns_prefix *prev;
46   struct xml_node *e;                   /* Which element defined this prefix */
47   struct ns_hash_entry *he;             /* NULL if changing default NS */
48   uint prev_ns;                         /* Previous NS ID assigned to this prefix */
49 };
50
51 static bool
52 ns_enabled(struct xml_context *ctx)
53 {
54   return (ctx->flags & XML_NAMESPACES);
55 }
56
57 void
58 xml_ns_enable(struct xml_context *ctx)
59 {
60   if (ns_enabled(ctx))
61     return;
62
63   TRACE(ctx, "NS: Enabling");
64   ASSERT(!ctx->depth);
65   ctx->flags |= XML_NAMESPACES;
66   if (!ctx->ns_pool)
67     {
68       // CAVEAT: xml_reset() must handle everything we allocate here
69       TRACE(ctx, "NS: Allocating data structures");
70       ctx->ns_pool = mp_new(4096);
71       GARY_INIT(ctx->ns_by_id, 16);
72     }
73
74   ctx->ns_by_name = xml_hash_new(ctx->ns_pool, sizeof(struct ns_hash_table));
75   ns_hash_init(ctx->ns_by_name);
76
77   ctx->ns_by_prefix = xml_hash_new(ctx->ns_pool, sizeof(struct ns_hash_table));
78   ns_hash_init(ctx->ns_by_prefix);
79
80   /* Intern well-known namespaces */
81   GARY_RESIZE(ctx->ns_by_id, 0);
82   uint none_ns = xml_ns_by_name(ctx, "");
83   uint xmlns_ns = xml_ns_by_name(ctx, "http://www.w3.org/2000/xmlns/");
84   uint xml_ns = xml_ns_by_name(ctx, "http://www.w3.org/XML/1998/namespace");
85   ASSERT(none_ns == XML_NS_NONE && xmlns_ns == XML_NS_XMLNS && xml_ns == XML_NS_XML);
86
87   /* Intern standard prefixes */
88   int new_xmlns, new_xml;
89   ns_hash_lookup(ctx->ns_by_prefix, "xmlns", &new_xmlns)->ns = xmlns_ns;
90   ns_hash_lookup(ctx->ns_by_prefix, "xml", &new_xml)->ns = xml_ns;
91   ASSERT(new_xmlns && new_xml);
92 }
93
94 void
95 xml_ns_cleanup(struct xml_context *ctx)
96 {
97   if (!ctx->ns_pool)
98     return;
99
100   TRACE(ctx, "NS: Cleanup");
101   GARY_FREE(ctx->ns_by_id);
102   mp_delete(ctx->ns_pool);
103 }
104
105 void
106 xml_ns_reset(struct xml_context *ctx)
107 {
108   if (!ns_enabled(ctx))
109     return;
110
111   TRACE(ctx, "NS: Reset");
112   GARY_RESIZE(ctx->ns_by_id, 0);
113   mp_flush(ctx->ns_pool);
114 }
115
116 const char *
117 xml_ns_by_id(struct xml_context *ctx, uint ns)
118 {
119   if (!ns)              // This should work even if namespaces are disabled
120     return "";
121   ASSERT(ns_enabled(ctx) && ns < GARY_SIZE(ctx->ns_by_id));
122   return ctx->ns_by_id[ns];
123 }
124
125 uint
126 xml_ns_by_name(struct xml_context *ctx, const char *name)
127 {
128   ASSERT(ns_enabled(ctx));
129   int new_p;
130   struct ns_hash_entry *he = ns_hash_lookup(ctx->ns_by_name, (char *) name, &new_p);
131   if (new_p)
132     {
133       he->ns = GARY_SIZE(ctx->ns_by_id);
134       ASSERT(he->ns < ~0U);
135       *GARY_PUSH(ctx->ns_by_id) = he->name;
136       TRACE(ctx, "NS: New namespace <%s> with ID %u", he->name, he->ns);
137     }
138   return he->ns;
139 }
140
141 static struct xml_ns_prefix *
142 ns_push_prefix(struct xml_context *ctx)
143 {
144   struct xml_ns_prefix *px = mp_alloc(ctx->stack, sizeof(*px));
145   px->prev = ctx->ns_prefix_stack;
146   ctx->ns_prefix_stack = px;
147   px->e = ctx->node;
148   return px;
149 }
150
151 static uint
152 ns_resolve(struct xml_context *ctx, char **namep, uint default_ns)
153 {
154   char *name = *namep;
155   char *colon = strchr(name, ':');
156   if (colon)
157     {
158       *colon = 0;
159       struct ns_hash_entry *he = ns_hash_find(ctx->ns_by_prefix, name);
160       *colon = ':';
161       if (he && he->ns)
162         {
163           *namep = colon + 1;
164           return he->ns;
165         }
166       else
167         {
168           xml_error(ctx, "Unknown namespace prefix for %s", name);
169           return 0;
170         }
171     }
172   else
173     return default_ns;
174 }
175
176 void xml_ns_push_element(struct xml_context *ctx)
177 {
178   struct xml_node *e = ctx->node;
179   if (!ns_enabled(ctx))
180     {
181       e->ns = 0;
182       return;
183     }
184
185   /* Scan attributes for prefix definitions */
186   XML_ATTR_FOR_EACH(a, e)
187     if (str_has_prefix(a->name, "xmlns"))
188       {
189         uint ns = xml_ns_by_name(ctx, a->val);
190         if (a->name[5] == ':')
191           {
192             if (!ns)
193               xml_error(ctx, "Namespace prefixes must not be undeclared");
194             else if (a->name[6])
195               {
196                 /* New NS prefix */
197                 int new_p;
198                 struct ns_hash_entry *he = ns_hash_lookup(ctx->ns_by_prefix, a->name + 6, &new_p);
199                 if (new_p)
200                   he->ns = 0;
201                 struct xml_ns_prefix *px = ns_push_prefix(ctx);
202                 px->he = he;
203                 px->prev_ns = he->ns;
204                 he->ns = ns;
205                 TRACE(ctx, "NS: New prefix <%s> -> ID %u", he->name, he->ns);
206               }
207             else
208               xml_error(ctx, "Invalid namespace prefix");
209           }
210         else if (!a->name[5])
211           {
212             /* New default NS */
213             struct xml_ns_prefix *px = ns_push_prefix(ctx);
214             px->he = NULL;
215             px->prev_ns = ctx->ns_default;
216             ctx->ns_default = ns;
217             TRACE(ctx, "New default NS -> ID %u", ns);
218           }
219       }
220
221   /* Resolve namespaces */
222   e->ns = ns_resolve(ctx, &e->name, ctx->ns_default);
223   XML_ATTR_FOR_EACH(a, e)
224     a->ns = ns_resolve(ctx, &a->name, 0);
225 }
226
227 void xml_ns_pop_element(struct xml_context *ctx)
228 {
229   if (!ns_enabled(ctx))
230     return;
231
232   struct xml_ns_prefix *px;
233   while ((px = ctx->ns_prefix_stack) && px->e == ctx->node)
234     {
235       struct ns_hash_entry *he = px->he;
236       if (he)
237         {
238           TRACE(ctx, "NS: Restoring prefix <%s> -> ID %u", he->name, px->prev_ns);
239           he->ns = px->prev_ns;
240         }
241       else
242         {
243           TRACE(ctx, "NS: Restoring default NS -> ID %u", px->prev_ns);
244           ctx->ns_default = px->prev_ns;
245         }
246       ctx->ns_prefix_stack = px->prev;
247     }
248 }