diff options
author | sternenseemann <sternenseemann@systemli.org> | 2020-08-11 14:58:57 +0200 |
---|---|---|
committer | sternenseemann <sternenseemann@systemli.org> | 2020-08-11 14:58:57 +0200 |
commit | 6c51d03ca0b2acb182ae7d168178ba192476c2c7 (patch) | |
tree | 95f1b1ebd7addea5d1db32c88b6e6018e7ce5f58 | |
parent | 5e41e6e81915801da14d0ef582fb7376b77247b4 (diff) |
feat(xml): add support for CDATA sections
* xml_open_cdata and xml_close_cdata add convenient support for this * tracked on stack using the newly introduced field type which is of enum xml_tag_type which may either be XML_NORMAL_TAG or XML_CDATA. * xml_close_all and xml_close_including also support closing CDATA sections now * xml_close_including now behaves like xml_close_all if NULL is given as tag, xml_close_all newly aliased to xml_close_including(..., NULL)
-rw-r--r-- | main.c | 4 | ||||
-rw-r--r-- | xml.c | 96 | ||||
-rw-r--r-- | xml.h | 58 |
3 files changed, 131 insertions, 27 deletions
diff --git a/main.c b/main.c index 6f29d10..4af412e 100644 --- a/main.c +++ b/main.c @@ -325,9 +325,9 @@ void blog_rss(char script_name[]) { if(entries[i].text_size > 0) { xml_open_tag(&ctx, "description"); - xml_raw(&ctx, "<![CDATA["); + xml_open_cdata(&ctx); xml_raw(&ctx, entries[i].text); - xml_raw(&ctx, "]]>"); + xml_close_cdata(&ctx); xml_close_tag(&ctx, "description"); } diff --git a/xml.c b/xml.c index ca319c9..06ad654 100644 --- a/xml.c +++ b/xml.c @@ -176,6 +176,7 @@ void xml_open_tag_attrs(struct xml_context *ctx, const char *tag, size_t attr_co ctx->stack->next = old_stack; size_t tag_size = strlen(tag) + 1; + ctx->stack->type = XML_NORMAL_TAG; ctx->stack->tag = malloc(sizeof(char) * tag_size); memcpy(ctx->stack->tag, tag, tag_size); } @@ -190,13 +191,18 @@ void xml_close_tag(struct xml_context *ctx, const char *tag) { return; } - if(ctx->stack == NULL || strcmp(tag, ctx->stack->tag) != 0) { - DEBUG_WARN(ctx, "Refusing to close tag %s, ", tag); - if(ctx->stack == NULL) { - DEBUG_WARN(ctx, "no tags left to be closed\n"); - } else { - DEBUG_WARN(ctx, "unclosed tags remaining\n"); - } + if(ctx->stack == NULL) { + DEBUG_WARN(ctx, "Refusing to close tag %s, no tags left to be closed\n", tag); + return; + } + + if(ctx->stack->type != XML_NORMAL_TAG) { + DEBUG_WARN(ctx, "Refusing to close tag %s, wrong tag type\n", tag); + return; + } + + if(strcmp(tag, ctx->stack->tag) != 0) { + DEBUG_WARN(ctx, "Refusing to close tag %s, unclosed tags remaining\n", tag); return; } @@ -213,35 +219,91 @@ void xml_close_tag(struct xml_context *ctx, const char *tag) { } void xml_close_all(struct xml_context *ctx) { + xml_close_including(ctx, NULL); +} + +void xml_close_including(struct xml_context *ctx, const char *tag) { if(ctx == NULL) { DEBUG_WARN(ctx, "Got no ctx\n"); return; } if(ctx->stack == NULL) { + if(tag != NULL) { + DEBUG_WARN(ctx, "Hit end of tag stack while searching for tag %s to close\n", tag); + } return; } else { - xml_close_tag(ctx, ctx->stack->tag); - xml_close_all(ctx); + int last_tag = tag != NULL && strcmp(tag, ctx->stack->tag) == 0; + + switch(ctx->stack->type) { + case XML_NORMAL_TAG: + xml_close_tag(ctx, ctx->stack->tag); + break; + case XML_CDATA: + xml_close_cdata(ctx); + break; + default: + DEBUG_WARN(ctx, "Unexpected tag type on stack, aborting\n"); + return; + } + + if(!last_tag) { + xml_close_including(ctx, tag); + } } } -void xml_close_including(struct xml_context *ctx, const char *tag) { +void xml_open_cdata(struct xml_context *ctx) { if(ctx == NULL) { DEBUG_WARN(ctx, "Got no ctx\n"); return; } + struct xml_stack *old_stack = ctx->stack; + + ctx->stack = malloc(sizeof(struct xml_stack)); + if(ctx->stack == NULL) { - DEBUG_WARN(ctx, "Hit end of tag stack while searching for tag %s to close\n", tag); + ctx->stack = old_stack; + + DEBUG_WARN(ctx, "Could not allocate memory for tag stack, now everything will break.\n"); return; - } else { - int last_tag = strcmp(tag, ctx->stack->tag) == 0; + } - xml_close_tag(ctx, ctx->stack->tag); + ctx->stack->next = old_stack; + ctx->stack->tag = NULL; + ctx->stack->type = XML_CDATA; - if(!last_tag) { - xml_close_including(ctx, tag); - } + fputs("<![CDATA[", ctx->out); +} + +void xml_close_cdata(struct xml_context *ctx) { + if(ctx == NULL) { + DEBUG_WARN(ctx, "Got no ctx\n"); + return; + } + + if(ctx->stack == NULL) { + DEBUG_WARN(ctx, "No CDATA to close\n"); + return; } + + if(ctx->stack->type != XML_CDATA) { + DEBUG_WARN(ctx, "No CDATA on top of stack, refusing to close\n"); + return; + } + + struct xml_stack *old_head = ctx->stack; + + ctx->stack = old_head->next; + + if(old_head->tag != NULL) { + // shouldn't happen though + free(old_head->tag); + } + + free(old_head); + + fputs("]]>", ctx->out); } diff --git a/xml.h b/xml.h index be43faa..869a579 100644 --- a/xml.h +++ b/xml.h @@ -14,15 +14,13 @@ * programmer's XML nesting. For information on its sanity * checking abilities see the documentation of `xml_close_tag()`. * - * Currently it has some limitations: + * Currently it has some limitations (possibly incomplete list): * * * It does not give the calling code feedback if errors occurred * * It doesn't do validity checking of tags and attributes * (legal characters etc.) * * It can't generate pretty output (i. e. properly indented), * its output is currently always "minified". - * * It currently has no satisfying support for `CDATA`, - * XML declarations and probably a lot more * * For handling arbitrary data this library is probably not a good * fit, it is mainly intended and tested for generating HTML and @@ -37,6 +35,17 @@ #include <stdio.h> /*! + * @brief Type of an XML "tag" + * + * This is mostly internally used to be able + * to keep track of CDATA using `xml_stack`. + */ +enum xml_tag_type { + XML_NORMAL_TAG, + XML_CDATA +}; + +/*! * @brief Internal linked list type * * Linked list used internally to keep track of tags to close. @@ -44,7 +53,8 @@ * @see struct xml_context */ struct xml_stack { - char *tag; //!< tag name + enum xml_tag_type type; //! type of the tag + char *tag; //!< tag name if `XML_NORMAL_TAG`, otherwise `NULL` struct xml_stack *next; //!< tag to be closed after the current one }; @@ -241,9 +251,11 @@ void xml_close_tag(struct xml_context *ctx, const char *tag); * @brief Close all remaining unclosed tags * * `xml_close_all()` iterates through the `xml_stack` and calls - * `xml_close_tag()` for every tag in it. A call to it will thus - * result in an empty `xml_stack` and all previously opened tags - * being closed correctly. + * `xml_close_tag()` or `xml_close_cdata()` respectively for every + * entry in it. A call to it will thus result in an empty `xml_stack` + * and all previously opened tags being closed correctly. + * + * Internally it's an alias for `xml_close_all(ctx, NULL)` * * Note that `xml_close_all()` will limit error checking, since it * (by nature) always succeeds and has no insight into what the @@ -261,7 +273,8 @@ void xml_close_all(struct xml_context *ctx); * `xml_close_including()` works like `xml_close_all()`, but * will stop after it hits a tag of the given name. * If the given tag is not present in the stack, it behaves - * like `xml_close_all()`. + * like `xml_close_all()`. It is not possible to match + * a `CDATA` section using `xml_close_including()`. * * Be aware that it might lead to unexpected results if * multiple tags of the same are nested. Consider the @@ -296,3 +309,32 @@ void xml_close_all(struct xml_context *ctx); * @see struct xml_stack */ void xml_close_including(struct xml_context *ctx, const char *tag); + +/*! + * @brief Start CDATA section + * + * Behaves like xml_open_tag(), but for opening `CDATA` sections. + * Internally the `XML_CDATA` type of `struct xml_stack` is used. + * + * Note that this function won't prevent `CDATA` sections or XML + * elements inside a `CDATA` section, since this is sometimes + * useful. + * + * @see xml_close_cdata + * @see enum xml_tag_type + * @see struct xml_stack + */ +void xml_open_cdata(struct xml_context *ctx); + +/*! + * @brief Close CDATA section + * + * Behaves like xml_close_tag(), but for `CDATA` sections. + * + * Checks the top of the stack if it is a `CDATA` section. + * In that case closes it and updates the stack, otherwise + * does nothing and if applicable outputs a warning. + * + * @see xml_open_cdata + */ +void xml_close_cdata(struct xml_context *ctx); |