Workshop o mikrokontrolérech na SKSP 2024.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

249 lines
5.9 KiB

2 months ago
/*
* UCW Library -- A simple XML parser -- Namespaces
*
* (c) 2015 Martin Mares <mj@ucw.cz>
*
* This software may be freely distributed and used according to the terms
* of the GNU Lesser General Public License.
*/
#undef LOCAL_DEBUG
#include <ucw/lib.h>
#include <ucw/gary.h>
#include <ucw/string.h>
#include <ucw-xml/xml.h>
#include <ucw-xml/internals.h>
/*
* This is an implementation of XML namespaces according to
* http://www.w3.org/TR/REC-xml-names/.
*
* Currently, we assume that the document does not contain a plethora
* of namespaces and prefixes. So we keep them in memory until the
* document ends.
*/
struct ns_hash_entry {
uint ns;
char name[1];
};
#define HASH_NODE struct ns_hash_entry
#define HASH_PREFIX(x) ns_hash_##x
#define HASH_KEY_ENDSTRING name
#define HASH_WANT_FIND
#define HASH_WANT_LOOKUP
#define HASH_TABLE_DYNAMIC
#define HASH_TABLE_ALLOC
#define HASH_LOOKUP_DETECT_NEW
#define HASH_GIVE_ALLOC
XML_HASH_GIVE_ALLOC
#include <ucw/hashtable.h>
struct xml_ns_prefix {
struct xml_ns_prefix *prev;
struct xml_node *e; /* Which element defined this prefix */
struct ns_hash_entry *he; /* NULL if changing default NS */
uint prev_ns; /* Previous NS ID assigned to this prefix */
};
static bool
ns_enabled(struct xml_context *ctx)
{
return (ctx->flags & XML_NAMESPACES);
}
void
xml_ns_enable(struct xml_context *ctx)
{
if (ns_enabled(ctx))
return;
TRACE(ctx, "NS: Enabling");
ASSERT(!ctx->depth);
ctx->flags |= XML_NAMESPACES;
if (!ctx->ns_pool)
{
// CAVEAT: xml_reset() must handle everything we allocate here
TRACE(ctx, "NS: Allocating data structures");
ctx->ns_pool = mp_new(4096);
GARY_INIT(ctx->ns_by_id, 16);
}
ctx->ns_by_name = xml_hash_new(ctx->ns_pool, sizeof(struct ns_hash_table));
ns_hash_init(ctx->ns_by_name);
ctx->ns_by_prefix = xml_hash_new(ctx->ns_pool, sizeof(struct ns_hash_table));
ns_hash_init(ctx->ns_by_prefix);
/* Intern well-known namespaces */
GARY_RESIZE(ctx->ns_by_id, 0);
uint none_ns = xml_ns_by_name(ctx, "");
uint xmlns_ns = xml_ns_by_name(ctx, "http://www.w3.org/2000/xmlns/");
uint xml_ns = xml_ns_by_name(ctx, "http://www.w3.org/XML/1998/namespace");
ASSERT(none_ns == XML_NS_NONE && xmlns_ns == XML_NS_XMLNS && xml_ns == XML_NS_XML);
/* Intern standard prefixes */
int new_xmlns, new_xml;
ns_hash_lookup(ctx->ns_by_prefix, "xmlns", &new_xmlns)->ns = xmlns_ns;
ns_hash_lookup(ctx->ns_by_prefix, "xml", &new_xml)->ns = xml_ns;
ASSERT(new_xmlns && new_xml);
}
void
xml_ns_cleanup(struct xml_context *ctx)
{
if (!ctx->ns_pool)
return;
TRACE(ctx, "NS: Cleanup");
GARY_FREE(ctx->ns_by_id);
mp_delete(ctx->ns_pool);
}
void
xml_ns_reset(struct xml_context *ctx)
{
if (!ns_enabled(ctx))
return;
TRACE(ctx, "NS: Reset");
GARY_RESIZE(ctx->ns_by_id, 0);
mp_flush(ctx->ns_pool);
}
const char *
xml_ns_by_id(struct xml_context *ctx, uint ns)
{
if (!ns) // This should work even if namespaces are disabled
return "";
ASSERT(ns_enabled(ctx) && ns < GARY_SIZE(ctx->ns_by_id));
return ctx->ns_by_id[ns];
}
uint
xml_ns_by_name(struct xml_context *ctx, const char *name)
{
ASSERT(ns_enabled(ctx));
int new_p;
struct ns_hash_entry *he = ns_hash_lookup(ctx->ns_by_name, (char *) name, &new_p);
if (new_p)
{
he->ns = GARY_SIZE(ctx->ns_by_id);
ASSERT(he->ns < ~0U);
*GARY_PUSH(ctx->ns_by_id) = he->name;
TRACE(ctx, "NS: New namespace <%s> with ID %u", he->name, he->ns);
}
return he->ns;
}
static struct xml_ns_prefix *
ns_push_prefix(struct xml_context *ctx)
{
struct xml_ns_prefix *px = mp_alloc(ctx->stack, sizeof(*px));
px->prev = ctx->ns_prefix_stack;
ctx->ns_prefix_stack = px;
px->e = ctx->node;
return px;
}
static uint
ns_resolve(struct xml_context *ctx, char **namep, uint default_ns)
{
char *name = *namep;
char *colon = strchr(name, ':');
if (colon)
{
*colon = 0;
struct ns_hash_entry *he = ns_hash_find(ctx->ns_by_prefix, name);
*colon = ':';
if (he && he->ns)
{
*namep = colon + 1;
return he->ns;
}
else
{
xml_error(ctx, "Unknown namespace prefix for %s", name);
return 0;
}
}
else
return default_ns;
}
void xml_ns_push_element(struct xml_context *ctx)
{
struct xml_node *e = ctx->node;
if (!ns_enabled(ctx))
{
e->ns = 0;
return;
}
/* Scan attributes for prefix definitions */
XML_ATTR_FOR_EACH(a, e)
if (str_has_prefix(a->name, "xmlns"))
{
uint ns = xml_ns_by_name(ctx, a->val);
if (a->name[5] == ':')
{
if (!ns)
xml_error(ctx, "Namespace prefixes must not be undeclared");
else if (a->name[6])
{
/* New NS prefix */
int new_p;
struct ns_hash_entry *he = ns_hash_lookup(ctx->ns_by_prefix, a->name + 6, &new_p);
if (new_p)
he->ns = 0;
struct xml_ns_prefix *px = ns_push_prefix(ctx);
px->he = he;
px->prev_ns = he->ns;
he->ns = ns;
TRACE(ctx, "NS: New prefix <%s> -> ID %u", he->name, he->ns);
}
else
xml_error(ctx, "Invalid namespace prefix");
}
else if (!a->name[5])
{
/* New default NS */
struct xml_ns_prefix *px = ns_push_prefix(ctx);
px->he = NULL;
px->prev_ns = ctx->ns_default;
ctx->ns_default = ns;
TRACE(ctx, "New default NS -> ID %u", ns);
}
}
/* Resolve namespaces */
e->ns = ns_resolve(ctx, &e->name, ctx->ns_default);
XML_ATTR_FOR_EACH(a, e)
a->ns = ns_resolve(ctx, &a->name, 0);
}
void xml_ns_pop_element(struct xml_context *ctx)
{
if (!ns_enabled(ctx))
return;
struct xml_ns_prefix *px;
while ((px = ctx->ns_prefix_stack) && px->e == ctx->node)
{
struct ns_hash_entry *he = px->he;
if (he)
{
TRACE(ctx, "NS: Restoring prefix <%s> -> ID %u", he->name, px->prev_ns);
he->ns = px->prev_ns;
}
else
{
TRACE(ctx, "NS: Restoring default NS -> ID %u", px->prev_ns);
ctx->ns_default = px->prev_ns;
}
ctx->ns_prefix_stack = px->prev;
}
}