xml.h

Streaming XML/HTML pull parser for lwIP-CE.

Pull/event-style parser. The caller owns a small ring buffer, feeds raw bytes via xml_take(), then pulls events with xml_next(). Event strings are copied into fixed-size fields in xml_event_t, so events remain valid after the parser advances.

Author

Anthony Cagliano

Author

Claude Code

Streaming: xml_next() returns XML_ERR_NEED_MORE when the ring does not yet contain a complete token. Feed more bytes with xml_take() and retry.

Lax/HTML mode (XML_FLAG_LAX):

  • Tag names and attribute names are lowercased.

  • Void elements (

    , , etc.) auto-synthesize an ELEMENT_END.

  • Unquoted attribute values are accepted.

  • Boolean attributes (no =value) are accepted (value == “”).

  • Bare & not forming a recognized entity is passed through as ‘&’.

  • <!DOCTYPE …> declarations are skipped.

Known constraints:

  • No namespace support.

  • No DTD or entity expansion beyond the built-in XML/HTML entities.

  • Processing instructions and comments are skipped silently.

  • Maximum element nesting depth: XML_MAX_DEPTH.

  • Maximum attributes per element: XML_MAX_ATTRS.

  • Names longer than XML_NAME_MAX are truncated.

  • Text longer than XML_TEXT_MAX is truncated (remainder discarded until the next tag boundary).

  • Attribute values longer than XML_ATTR_VAL_MAX are truncated.

Defines

XML_MAX_DEPTH
XML_MAX_ATTRS
XML_NAME_MAX
XML_TEXT_MAX
XML_ATTR_VAL_MAX
XML_FLAG_LAX

Typedefs

typedef xml_ctx_t xml_parser_t

Enums

enum xml_err_t

Values:

enumerator XML_OK
enumerator XML_ERR_INVALID
enumerator XML_ERR_TRUNCATED
enumerator XML_ERR_DEPTH
enumerator XML_ERR_DONE
enumerator XML_ERR_NEED_MORE
enum xml_event_type_t

Values:

enumerator XML_EVT_NONE
enumerator XML_EVT_ELEMENT_START
enumerator XML_EVT_ELEMENT_END
enumerator XML_EVT_TEXT

Functions

void xml_init(xml_ctx_t *ctx, char *ring, size_t ring_len, uint8_t flags)

Initialize the parser.

Parameters:
  • ctx – Parser context (caller-allocated, may be on stack or BSS).

  • ring – Caller-provided ring buffer (must remain valid).

  • ring_len – Size of ring in bytes. Should be at least a few hundred bytes; larger rings tolerate burstier input.

  • flags – 0 or XML_FLAG_LAX.

void xml_finish(xml_ctx_t *ctx)

Signal end of input stream.

After calling this, xml_next() will flush any remaining accumulated text and return XML_ERR_DONE once the ring is empty. Without this call, xml_next() returns XML_ERR_NEED_MORE whenever the ring drains in CONTENT state (since it cannot tell whether more bytes are coming).

Call xml_finish() after the last xml_take() when no more data will arrive.

size_t xml_take(xml_ctx_t *ctx, const char *buf, size_t len)

Feed raw bytes into the parser’s ring buffer.

Non-blocking: if the ring is full, only as many bytes as fit are consumed. Returns the number of bytes actually written. The caller must retry any unwritten tail after draining events with xml_next().

xml_err_t xml_next(xml_ctx_t *ctx, xml_event_t *evt)

Pull the next parsed event.

Returns: XML_OK — *evt is valid. XML_ERR_NEED_MORE — ring does not yet contain a complete token; feed more bytes with xml_take() and call xml_next() again. XML_ERR_DONE — input cleanly exhausted (ring empty, no partial token). XML_ERR_DEPTH — element nesting exceeded XML_MAX_DEPTH. XML_ERR_INVALID — malformed input (strict mode only; lax mode recovers).

bool xml_get_attr(const xml_event_t *evt, const char *name, char *dst, size_t dst_len)

Find an attribute by name in an event returned by xml_next().

Copies the value (NUL-terminated) into dst. Returns true on match, false if not present or dst too small.

xml_err_t xml_skip(xml_ctx_t *ctx)

Skip the current element body.

Call immediately after an XML_EVT_ELEMENT_START event for a non-self-closing element. The parser consumes events until the matching XML_EVT_ELEMENT_END.

xml_err_t xml_get_inner_text(xml_ctx_t *ctx, char *dst, size_t dst_len)

Copy the immediate text content of the current element, then consume that element’s closing XML_EVT_ELEMENT_END.

size_t xml_list_attrs(const xml_event_t *evt, char *dst, size_t dst_len)

Build a comma-separated name=value list from an element event’s attributes.

Returns the bytes written, excluding NUL, or (size_t)-1 if dst is too small.

bool xml_slice_copy(char *dst, size_t dst_len, const xml_slice_t *s)

Copy a raw XML slice into a NUL-terminated buffer.

bool xml_decode_entity(const char **src, const char *src_end, char *dst, size_t *dst_written)

Decode a single XML entity reference at *src (must point to ‘&’).

Writes the decoded character into *dst and sets *dst_written to 1. Advances *src past the closing ‘;’. Returns false if unrecognized or malformed; *src is unchanged.

Supported: & < > ‘ “

struct xml_attr_t
#include <xml.h>

Public Members

char name[XML_NAME_MAX]
char value[XML_ATTR_VAL_MAX]
struct xml_slice_t
#include <xml.h>

Public Members

const char *str
size_t len
struct xml_event_t
#include <xml.h>

Public Members

xml_event_type_t type
char name[XML_NAME_MAX]
char text[XML_TEXT_MAX]
xml_attr_t attrs[XML_MAX_ATTRS]
uint8_t attr_count
bool self_closing
struct xml_ctx_t
#include <xml.h>

Public Members

char *ring
size_t ring_len
size_t wpos
size_t rpos
uint8_t flags
uint8_t depth
uint8_t state
char tag_name[XML_NAME_MAX]
uint8_t tag_name_len
bool tag_closing
xml_attr_t cur_attr
uint8_t cur_attr_name_len
uint8_t cur_attr_val_len
char attr_quote
bool attr_bool
xml_event_t pending
bool has_pending
bool void_end_pending
char void_end_name[XML_NAME_MAX]
bool finished
char text_buf[XML_TEXT_MAX]
uint8_t text_len
bool text_overflow
char entity_buf[12]
uint8_t entity_len