1 /* zxns.c - Namespace manipulation functions for generated (and other) code
2 * Copyright (c) 2010-2011 Sampo Kellomaki (sampo@iki.fi), All Rights Reserved.
3 * Copyright (c) 2006-2008 Symlabs (symlabs@symlabs.com), All Rights Reserved.
4 * Author: Sampo Kellomaki (sampo@iki.fi)
5 * This is confidential unpublished proprietary source code of the author.
6 * NO WARRANTY, not even implied warranties. Contains trade secrets.
7 * Distribution prohibited unless authorized in writing.
8 * Licensed under Apache License 2.0, see file COPYING.
9 * $Id: zxns.c,v 1.7 2009-08-22 13:56:34 sampo Exp $
10 *
11 * 28.5.2006, created --Sampo
12 * 8.8.2006, moved lookup functions to generated code --Sampo
13 * 12.8.2006, added special scanning of xmlns to avoid backtracking elem recognition --Sampo
14 * 26.8.2006, significant Common Subexpression Elimination (CSE) --Sampo
15 * 30.9.2007, rework namespaces, esp. previously unknown namespaces --Sampo
16 * 7.10.2008, added documentation --Sampo
17 * 27.10.2010, re-engineered namespace handling --Sampo
18 */
19
20 #include <string.h>
21 #include <stdio.h>
22 #include "platform.h"
23 #include "errmac.h"
24 #include "zx.h"
25 #include "c/zx-data.h"
26 #include "c/zx-ns.h"
27
28 /* ------------- All the manner namespace management ------------- */
29
30 #if 0
31 /* Called by: */
32 void zx_fix_any_elem_dec(struct zx_ctx* c, struct zx_elem_s* x, const char* nam, int namlen)
33 {
34 char* p = memchr(nam, ':', namlen);
35 if (p) {
36 x->gg.g.ns = zx_locate_ns_by_prefix(c, p-nam, nam);
37 ++p;
38 namlen -= p-nam;
39 nam = p;
40 }
41 x->name = (char*)nam;
42 x->name_len = namlen;
43 }
44
45 /*() Given known namespace, does the prefix refer to it, either natively or through an alias. */
46
47 /* Called by: */
48 int zx_is_ns_prefix(struct zx_ns_s* ns, int len, const char* prefix)
49 {
50 struct zx_ns_s* alias;
51 if (!ns)
52 return 0;
53 if (ns->prefix_len == len && (!len || !memcmp(ns->prefix, prefix, len)))
54 return 1;
55 for (alias = ns->n; alias; alias = alias->n)
56 if (alias->prefix_len == len && (!len || !memcmp(alias->prefix, prefix, len)))
57 return 1;
58 return 0;
59 }
60 #endif
61
62 /*() Debugging function. You can say in gdb: print zx_dump_ns_tab(c,0) */
63
64 /* Called by: so_enc_dec */
zx_dump_ns_tab(struct zx_ctx * c,int flags)65 int zx_dump_ns_tab(struct zx_ctx* c, int flags)
66 {
67 struct zx_ns_s* ns;
68 struct zx_ns_s* alias;
69 int n_ns = c->n_ns;
70 int n=0;
71 for (ns = c->ns_tab; n < n_ns; ++ns) {
72 fprintf(stdout, "%3d NS %8.*s %.*s\n", ++n, ns->prefix_len, ns->prefix, ns->url_len, ns->url);
73 for (alias = ns->n; alias; alias = alias->n)
74 fprintf(stdout, "%3d A %8.*s %.*s\n", ++n, alias->prefix_len, alias->prefix, alias->url_len, alias->url);
75 }
76 for (alias = c->unknown_ns; alias; alias = alias->n) {
77 fprintf(stdout, "%3d UNK %8.*s %.*s\n", ++n, alias->prefix_len, alias->prefix, alias->url_len, alias->url);
78 }
79 return n;
80 }
81
82 /* Profiling of 0.69 on 20101024 shows that 31% of the enc-dec time is spent here.
83 * Surprisingly, the namespace is never found! The only return that executes is 0.
84 * Input was t/azrq1.xml, where in our understanding all prefixes are known.
85 * After fixing a bug related to xmlns declarations being fed to here, the function
86 * was never called. Could we safely delete this? */
87
88 /* Called by: zx_fix_any_elem_dec, zx_init_tok_tab, zx_prefix_seen_whine, zx_xmlns_decl */
zx_locate_ns_by_prefix(struct zx_ctx * c,int len,const char * prefix)89 static struct zx_ns_s* zx_locate_ns_by_prefix(struct zx_ctx* c, int len, const char* prefix)
90 {
91 struct zx_ns_s* ns;
92 struct zx_ns_s* alias;
93 for (ns = c->unknown_ns; ns; ns = ns->n)
94 if (ns->prefix_len == len && (!len || !memcmp(ns->prefix, prefix, len)))
95 return ns;
96 for (ns = c->ns_tab; ns->url_len; ++ns) {
97 if (ns->prefix_len == len && (!len || !memcmp(ns->prefix, prefix, len)))
98 return ns;
99 for (alias = ns->n; alias; alias = alias->n)
100 if (alias->prefix_len == len && (!len || !memcmp(alias->prefix, prefix, len)))
101 return ns;
102 }
103 return 0;
104 }
105
106 /* Profiling of 0.69 on 20101024 shows that 64% of the enc-dec time is spent here.
107 * Here the namespace is always found via return ns, but not before searching through
108 * oodles of aliases. After bug fix, this consumes 95% of the zxencdectest run time!
109 * After further bug fixing eliminating scanning (and accumulation) of aliases, the run time went
110 * down by two orders of magnitude. Final solution is the namespace hash lookup. */
111
112 /* Called by: zx_xmlns_decl */
zx_locate_ns_by_url(struct zx_ctx * c,int len,const char * url)113 static struct zx_ns_s* zx_locate_ns_by_url(struct zx_ctx* c, int len, const char* url)
114 {
115 struct zx_ns_s* ns;
116 for (ns = c->unknown_ns; ns; ns = ns->n)
117 if (ns->url_len == len && (!len || !memcmp(ns->url, url, len)))
118 return ns;
119 return zx_url2ns(url, len); /* lookup in perfect hash generated by xsd2sg and gperf */
120 }
121
122 /* Called by: zx_xmlns_decl */
zx_new_known_ns(struct zx_ctx * c,int prefix_len,const char * prefix,struct zx_ns_s * known_ns)123 static struct zx_ns_s* zx_new_known_ns(struct zx_ctx* c, int prefix_len, const char* prefix, struct zx_ns_s* known_ns)
124 {
125 struct zx_ns_s* ns = ZX_ZALLOC(c, struct zx_ns_s);
126 ns->prefix_len = prefix_len;
127 ns->prefix = prefix;
128 ns->url_len = known_ns->url_len;
129 ns->url = known_ns->url;
130 ns->master = known_ns;
131 return ns;
132 }
133
134 /* Called by: zx_prefix_seen_whine, zx_xmlns_decl */
zx_new_unknown_ns(struct zx_ctx * c,int prefix_len,const char * prefix,int url_len,const char * url)135 static struct zx_ns_s* zx_new_unknown_ns(struct zx_ctx* c, int prefix_len, const char* prefix, int url_len, const char* url)
136 {
137 struct zx_ns_s* ns = ZX_ZALLOC(c, struct zx_ns_s);
138 ns->prefix_len = prefix_len;
139 ns->url_len = url_len;
140 /* Reallocating these is necessary for unknown namespaces */
141 ns->prefix = ZX_ALLOC(c, prefix_len+1);
142 if (prefix_len)
143 memcpy((char*)ns->prefix, prefix, prefix_len);
144 *((char*)ns->prefix+prefix_len) = 0;
145 ns->url = ZX_ALLOC(c, url_len+1);
146 if (url_len)
147 memcpy((char*)ns->url, url, url_len);
148 *((char*)ns->url+url_len) = 0;
149 return ns;
150 }
151
152 /*() Process XML namespace declaration, trying to match it by its declared namespace URI.
153 * Should this fail, we will attempt to match by customary (at least in our opinion)
154 * namespace prefixes. If duplicate namespaces are detected, they are handled as aliases. */
155
156 /* Called by: zx_push_seen x2 */
zx_xmlns_decl(struct zx_ctx * c,int prefix_len,const char * prefix,int url_len,const char * url)157 static struct zx_ns_s* zx_xmlns_decl(struct zx_ctx* c, int prefix_len, const char* prefix, int url_len, const char* url)
158 {
159 struct zx_ns_s* alias;
160 struct zx_ns_s* new_ns;
161 struct zx_ns_s* ns;
162
163 ns = zx_locate_ns_by_url(c, url_len, url);
164 if (!ns) {
165 D("Namespace(%.*s) not found by URL(%.*s) (probably unknown or wrong namespace URL)", prefix_len, prefix, url_len, url);
166 zx_xml_parse_dbg(c, '-', __FUNCTION__, "Namespace here");
167 ns = zx_locate_ns_by_prefix(c, prefix_len, prefix);
168 if (!ns) {
169 /* Namespace not known by compiled in schemata. */
170 D("Namespace not found by prefix(%.*s) or URL(%.*s) (probably unknown or wrong namespace URL)", prefix_len, prefix, url_len, url);
171 new_ns = zx_new_unknown_ns(c, prefix_len, prefix, url_len, url);
172 new_ns->n = c->unknown_ns;
173 c->unknown_ns = new_ns;
174 return new_ns;
175 }
176 }
177 /* Always alloc a new one because we may need to push to stack multiple instances of same ns. */
178 new_ns = zx_new_known_ns(c, prefix_len, prefix, ns);
179
180 /* Avoid adding to alias list if prefix is already known. */
181 if (ns->prefix_len == prefix_len && (!prefix_len || !memcmp(ns->prefix, prefix, prefix_len)))
182 goto known_prefix;
183 for (alias = ns->n; alias; alias = alias->n)
184 if (alias->prefix_len == prefix_len && (!prefix_len || !memcmp(alias->prefix, prefix, prefix_len)))
185 goto known_prefix;
186
187 D("New prefix(%.*s) known URL(%.*s)", prefix_len, prefix, url_len, url);
188 ns->n = new_ns->n;
189 new_ns->n = ns;
190 known_prefix:
191 return new_ns;
192 }
193
194 /*() Scan the seen list (expected to be relatively short so scan
195 * is not a perfomance issue). The scan walks the doubly linked
196 * list of currently active seen prefixes (as opposed to seen_pop). */
197
198 /* Called by: zx_prefix_seen_whine, zx_push_seen, zxsig_validate */
zx_prefix_seen(struct zx_ctx * c,int len,const char * prefix)199 struct zx_ns_s* zx_prefix_seen(struct zx_ctx* c, int len, const char* prefix)
200 {
201 struct zx_ns_s* ns;
202 if (len == 3 && !memcmp(prefix, "xml", 3))
203 return zx_ns_tab + (zx_xml_NS >> ZX_TOK_NS_SHIFT);
204 for (ns = c->guard_seen_n.seen_n; ns->seen_n; ns = ns->seen_n)
205 if (ns->prefix_len == len && (!len || !memcmp(ns->prefix, prefix, len)))
206 return ns;
207 return 0;
208 }
209
210 #define zx_unknown_prefix "urn:zxid:unknown-ns-prefix"
211
212 /*() zx_prefix_seen_whine() is a good place to detect, and add stub for,
213 * wholly unknown prefixes. Seldom returns NULL, just always makes something up. */
214
215 /* Called by: zx_attr_lookup, zx_el_lookup */
zx_prefix_seen_whine(struct zx_ctx * c,int len,const char * prefix,const char * logkey,int mk_dummy_ns)216 struct zx_ns_s* zx_prefix_seen_whine(struct zx_ctx* c, int len, const char* prefix, const char* logkey, int mk_dummy_ns)
217 {
218 struct zx_str* url;
219 struct zx_ns_s* ns = zx_prefix_seen(c, len, prefix);
220 if (!ns) {
221 /* Try to locate the namespace by the prefix from among the namespaces
222 * naturally known to zxid. */
223 ns = zx_locate_ns_by_prefix(c, len, prefix);
224 if (!ns) {
225 if (mk_dummy_ns) {
226 url = zx_strf(c, "%s:%s:%.*s", zx_unknown_prefix, logkey, len, STRNULLCHK(prefix));
227 ns = zx_new_unknown_ns(c, len, prefix, url->len, url->s);
228 ns->n = c->unknown_ns;
229 c->unknown_ns = ns;
230 D("Undefined namespace prefix(%.*s). NS not known from any context. Creating dummy ns(%.*s).", len, prefix, url->len, url->s);
231 zx_str_free(c, url);
232 } else {
233 D("Undefined namespace prefix(%.*s) at(%s). NS not known from any context.", len, prefix, logkey);
234 return 0;
235 }
236 } else {
237 if (len != 3 || memcmp(prefix, "xml", 3)) {
238 D("Undefined namespace prefix(%.*s) at(%s) mapped to uri(%.*s) by built-in table.", len, prefix, logkey, ns->url_len, ns->url);
239 }
240 }
241 }
242 return ns;
243 }
244
245 /*() See if prefix has been seen, and in the same meaning. If not, allocate
246 * a new node and push or add it to the doubly linked list as well as to the
247 * pop_seen list. Returns 0 if no addition was done (i.e. ns had been seen already). */
248
249 /* Called by: zx_add_xmlns_if_not_seen, zx_len_xmlns_if_not_seen, zx_scan_xmlns */
zx_push_seen(struct zx_ctx * c,int prefix_len,const char * prefix,int url_len,const char * url,struct zx_ns_s ** pop_seen)250 static struct zx_ns_s* zx_push_seen(struct zx_ctx* c, int prefix_len, const char* prefix, int url_len, const char* url, struct zx_ns_s** pop_seen)
251 {
252 struct zx_ns_s* old_ns;
253 struct zx_ns_s* ns;
254 old_ns = zx_prefix_seen(c, prefix_len, prefix);
255 if (old_ns) {
256 if (old_ns->url_len == url_len && (!url_len || !memcmp(old_ns->url, url, url_len)))
257 return 0;
258 ns = zx_xmlns_decl(c, prefix_len, prefix, url_len, url);
259 ns->seen = old_ns; /* Push */
260 ns->seen_n = old_ns->seen_n; /* Replace old_ns in middle of the list */
261 ns->seen_p = old_ns->seen_p;
262 old_ns->seen_n->seen_p = ns;
263 old_ns->seen_p->seen_n = ns;
264 } else {
265 ns = zx_xmlns_decl(c, prefix_len, prefix, url_len, url);
266 ns->seen_n = c->guard_seen_n.seen_n; /* Add to beginning of seen_n list. */
267 c->guard_seen_n.seen_n = ns;
268 ns->seen_n->seen_p = ns;
269 ns->seen_p = &c->guard_seen_n;
270 }
271 ns->seen_pop = *pop_seen;
272 *pop_seen = ns;
273 return ns;
274 }
275
276 /* Called by: TXENC_SO_ELNAME, TXLEN_SO_ELNAME, zx_DEC_elem, zx_ENC_WO_any_elem, zx_LEN_WO_any_elem, zxsig_validate */
zx_pop_seen(struct zx_ns_s * ns)277 void zx_pop_seen(struct zx_ns_s* ns)
278 {
279 for (; ns; ns = ns->seen_pop) {
280 if (ns->seen) {
281 ns->seen->seen_n = ns->seen_n; /* Replace ns with old_ns (ns->seen) in middle of list */
282 ns->seen->seen_p = ns->seen_p;
283 ns->seen_n->seen_p = ns->seen;
284 ns->seen_p->seen_n = ns->seen;
285 } else {
286 ns->seen_n->seen_p = ns->seen_p; /* Remove ns from middle of the seen_n list. */
287 ns->seen_p->seen_n = ns->seen_n;
288 }
289 }
290 }
291
292 /*() Collect namespaces from element */
293
294 /* Called by: sig_validate x3, zxid_chk_sig, zxid_sp_dig_oauth_sso_a7n, zxid_sp_dig_sso_a7n, zxid_sp_sso_finalize */
zx_see_elem_ns(struct zx_ctx * c,struct zx_ns_s ** pop_seen,struct zx_elem_s * el)295 void zx_see_elem_ns(struct zx_ctx* c, struct zx_ns_s** pop_seen, struct zx_elem_s* el)
296 {
297 struct zx_ns_s* ns;
298 for (ns = el->xmlns; ns; ns = ns->n) {
299 /*zx_push_seen(c, xmlns->);*/
300 ns->seen_n = c->guard_seen_n.seen_n; /* Add to beginning of seen_n list. */
301 c->guard_seen_n.seen_n = ns;
302 ns->seen_n->seen_p = ns;
303 ns->seen_p = &c->guard_seen_n;
304 ns->seen_pop = *pop_seen;
305 *pop_seen = ns;
306 }
307 }
308
309 /*() When trying to scan an element, an annoying feature of XML namespaces is that the
310 * namespace may be declared in a xmlns attribute within the element itself. Thus
311 * at the time of scanning the <ns:element part we can't know its namespace. What
312 * a lousy design. In order to handle this we need to either backtrack or
313 * make a special case forward scan for xmlns attributes (which is redundant with
314 * normal attribute scanning). It seems simpler to do the latter, so here goes...
315 * (besides, the forward scan will prime the cache)
316 *
317 * The return value represents the list of namespaces that were newly declared
318 * at this level, i.e. pushed to the seen stacks. This list is used to pop the
319 * seen stacks after we are through with the element. */
320
321 /* Called by: zx_el_lookup */
zx_scan_xmlns(struct zx_ctx * c)322 struct zx_ns_s* zx_scan_xmlns(struct zx_ctx* c)
323 {
324 struct zx_ns_s* pop_seen = 0; /* build a list of namespaces declared here */
325 const char* prefix;
326 const char* url;
327 const char* p = c->p; /* We need to keep the original c->p so normal attrs can be scanned. */
328 char quote;
329 int prefix_len, url_len;
330
331 /* The tag name has already been detected. Process attributes until '>' */
332
333 for ( ; 1; ++p) {
334 ZX_SKIP_WS_P(c, p, pop_seen);
335 if (ONE_OF_2(*p, '>', '/'))
336 break;
337 if (!memcmp(p, "xmlns", sizeof("xmlns")-1)) {
338 switch (p[5]) {
339 case '=': /* Default namespace decl. */
340 prefix = p += 5;
341 goto scan_URL;
342 case ':': /* Qualified namespace decl. */
343 prefix = p += 6;
344 quote = '=';
345 ZX_LOOK_FOR_P(c,'=',p);
346 scan_URL:
347 ++p;
348 if (!ONE_OF_2(*p, '"', '\''))
349 return pop_seen;
350 quote = *p;
351 url = ++p;
352 ZX_LOOK_FOR_P(c, quote, p);
353
354 prefix_len = (url - 2) - prefix;
355 url_len = p - url;
356 zx_push_seen(c, prefix_len, prefix, url_len, url, &pop_seen);
357 goto next;
358 default:
359 zx_xml_parse_err(c, p[5], (const char*)__FUNCTION__, "Illformed attributes. Bad char");
360 return pop_seen;
361 }
362 }
363
364 /* Skip over any other attributes. */
365
366 quote = '=';
367 ZX_LOOK_FOR_P(c,'=',p);
368 ++p;
369 if (!ONE_OF_2(*p, '"', '\''))
370 return pop_seen;
371 quote = *p;
372 ++p;
373 ZX_LOOK_FOR_P(c,quote,p);
374 next:
375 continue;
376 }
377 return pop_seen;
378
379 look_for_not_found:
380 zx_xml_parse_err(c, quote, (const char*)__FUNCTION__, "char not found");
381 return pop_seen;
382 }
383
384 #if 0 /* *** schedule to remove */
385 /*() Disambiguate token by considering its namespace.
386 * See zx_attr_lookup(), zx_elem_lookup()
387 * For attributes the namespaceless case is considered. */
388
389 /* Called by: */
390 const struct zx_tok* zx_tok_by_ns(const struct zx_tok* zt, const struct zx_tok* lim, int len, const char* name, struct zx_ns_s* ns)
391 {
392 struct zx_ns_s* alias;
393 const struct zx_tok* ztt;
394
395 /* First find token whose name matches. The token table CAN have duplicate entries,
396 * see -D flag to gperf. */
397 for (; zt < lim && (memcmp(zt->name, name, len) || zt->name[len]); ++zt) ;
398 if (zt >= lim)
399 return 0; /* Not found! */
400 ztt = zt; /* Remember first "hit" in case we fail. */
401
402 /* If no namespace was specified, then token must also be namespaceless */
403 if (!ns) {
404 if (!zt->ns)
405 return zt;
406 for (; zt < lim && (!memcmp(zt->name, name, len) && !zt->name[len]); ++zt)
407 if (!zt->ns)
408 return zt;
409 return ztt;
410 }
411 /* Now check for namespace match. */
412 for (; zt < lim && (!memcmp(zt->name, name, len) && !zt->name[len]); ++zt)
413 for (alias = zt->ns; alias; alias = alias->n)
414 if (alias == ns)
415 return zt;
416 return ztt;
417 }
418 #endif
419
420 #if 0
421 /*(-) N.B. Generally this function is not needed since tokens can be arranged
422 * to point to respective struct zx_ns_s at compile time using static
423 * initialization. This is handled by xsd2sg.pl */
424
425 /* Called by: */
426 int zx_init_tok_tab(struct zx_ctx* c, struct zx_tok* tok_tab, struct zx_tok* tok_tab_lim)
427 {
428 for (; tok_tab < tok_tab_lim; ++tok_tab)
429 if (!(tok_tab->ns = zx_locate_ns_by_prefix(c, strlen(tok_tab->prefix), (char*)tok_tab->prefix)))
430 return 0;
431 return 1;
432
433 /*if (!zx_init_tok_tab(&ctx, zx_elems, zx_elems + sizeof(zx_elems) / sizeof(struct zx_tok))) DIE("Inconsistency between tokens and namespaces");*/
434 }
435 #endif
436
437 /* Called by: zx_LEN_WO_any_elem x2, zx_len_inc_ns */
zx_len_xmlns_if_not_seen(struct zx_ctx * c,struct zx_ns_s * ns,struct zx_ns_s ** pop_seenp)438 int zx_len_xmlns_if_not_seen(struct zx_ctx* c, struct zx_ns_s* ns, struct zx_ns_s** pop_seenp)
439 {
440 if (!ns) {
441 DD("no ns %p", ns);
442 return 0;
443 }
444 if (!zx_push_seen(c, ns->prefix_len, ns->prefix, ns->url_len, ns->url, pop_seenp)) {
445 DD("no push seen %p", ns);
446 return 0;
447 }
448 /* Check for undeclared empty prefix, as seen in namespaceless XML */
449 if ((!ns->prefix || !*ns->prefix)
450 && !memcmp(ns->url, zx_unknown_prefix, sizeof(zx_unknown_prefix)-1)) {
451 DD("undeclared empty prefix %p", ns);
452 return 0;
453 }
454 DD("add xmlns %p", ns);
455 return sizeof(" xmlns")-1
456 + (ns->prefix_len ? ns->prefix_len+1 : 0) + 2 + ns->url_len + 1;
457 }
458
459 /*() For WO encoder the order of xmlns declarations is not known at compile
460 * time. Thus we first add them to the pop_seen list using insertion
461 * sort (pop_seen is smallest and prefixes grow from there) and
462 * then later render the list using zx_enc_seen(). */
463
464 /* Called by: zx_ENC_WO_any_elem, zx_add_inc_ns, zx_see_attr_ns */
zx_add_xmlns_if_not_seen(struct zx_ctx * c,struct zx_ns_s * ns,struct zx_ns_s ** pop_seenp)465 void zx_add_xmlns_if_not_seen(struct zx_ctx* c, struct zx_ns_s* ns, struct zx_ns_s** pop_seenp)
466 {
467 struct zx_ns_s* pop_seen_dummy=0;
468 struct zx_ns_s* new_ns;
469 int res;
470 if (!ns) {
471 DD("no ns %p", ns);
472 return;
473 }
474 new_ns = zx_push_seen(c, ns->prefix_len, ns->prefix, ns->url_len, ns->url, &pop_seen_dummy);
475 if (!new_ns) {
476 DD("no new_ns %p", ns);
477 return;
478 }
479 if (!*pop_seenp) {
480 *pop_seenp = new_ns;
481 DD("no pop_seen %p", ns);
482 return;
483 }
484 if (!new_ns->prefix_len) { /* Default namespace (empty prefix) sorts first. */
485 first:
486 new_ns->seen_pop = *pop_seenp;
487 *pop_seenp = new_ns;
488 DD("empty prefix %p", ns);
489 return;
490 }
491
492 ns = *pop_seenp;
493 if (ns->prefix_len) {
494 res = memcmp(ns->prefix, new_ns->prefix, MIN(ns->prefix_len, new_ns->prefix_len));
495 if (res > 0 || !res && ns->prefix_len >= new_ns->prefix_len)
496 goto first;
497 }
498 for (; ns->seen_pop; ns = ns->seen_pop) {
499 res = memcmp(ns->seen_pop->prefix, new_ns->prefix,
500 MIN(ns->seen_pop->prefix_len, new_ns->prefix_len));
501 if (res > 0 || !res && ns->seen_pop->prefix_len >= new_ns->prefix_len)
502 break;
503 }
504 new_ns->seen_pop = ns->seen_pop;
505 ns->seen_pop = new_ns;
506 }
507
508 /*() Render the namespaces that have been seen. The list is assumed to be already sorted. */
509
510 /* Called by: TXENC_SO_ELNAME, zx_ENC_WO_any_elem x2 */
zx_enc_seen(char * p,struct zx_ns_s * ns)511 char* zx_enc_seen(char* p, struct zx_ns_s* ns)
512 {
513 for (; ns; ns = ns->seen_pop) {
514 /* Check for undeclared empty prefix, as seen in namespaceless XML */
515 if ((!ns->prefix || !*ns->prefix)
516 && !memcmp(ns->url, zx_unknown_prefix, sizeof(zx_unknown_prefix)-1))
517 continue;
518 ZX_OUT_MEM(p, " xmlns", sizeof(" xmlns")-1);
519 if (ns->prefix_len) {
520 ZX_OUT_CH(p, ':');
521 ZX_OUT_MEM(p, ns->prefix, ns->prefix_len);
522 }
523 ZX_OUT_CH(p, '=');
524 ZX_OUT_CH(p, '"');
525 ZX_OUT_MEM(p, ns->url, ns->url_len);
526 ZX_OUT_CH(p, '"');
527 }
528 return p;
529 }
530
531 /*() dec-templ.c CSE elimination. */
532
533 /* Called by: zx_attr_lookup x2 */
zx_xmlns_detected(struct zx_ctx * c,struct zx_elem_s * x,const char * url)534 struct zx_ns_s* zx_xmlns_detected(struct zx_ctx* c, struct zx_elem_s* x, const char* url)
535 {
536 struct zx_ns_s* ns;
537 const char* prefix;
538 /* Scan backwards to find beginning of name. Already cached. Scan is cheaper than arg. */
539 for (prefix = url-2; !ONE_OF_5(*prefix, ':', ' ', '\n', '\r', '\t'); --prefix) ;
540 ++prefix;
541 DD("xmlns detected(%.*s)", url-2-prefix, prefix);
542 ns = ZX_ZALLOC(c, struct zx_ns_s);
543 ns->url = url;
544 ns->url_len = c->p - url;
545 ns->prefix_len = url-2-prefix;
546 ns->prefix = prefix;
547 ns->n = x->xmlns;
548 x->xmlns = ns;
549 return ns;
550 }
551
552 /* EOF -- zxns.c */
553