diff options
author | sternenseemann <sternenseemann@systemli.org> | 2020-08-11 13:41:33 +0200 |
---|---|---|
committer | sternenseemann <sternenseemann@systemli.org> | 2020-08-11 13:41:33 +0200 |
commit | f863b428f301e4b549308c06f2cf14c6ad287fb7 (patch) | |
tree | 017fc869f75baa771b1798036b14c0f252636d6f | |
parent | c82544f10ccf6a916f29d4a704c55f1eb9b12b62 (diff) |
feat(xml): support for escaping
BREAKING CHANGE: xml_open_tag_attrs() now outpus its attribute values xml escaped
-rw-r--r-- | doc/examples/xml_example.c | 27 | ||||
-rw-r--r-- | xml.c | 33 | ||||
-rw-r--r-- | xml.h | 33 |
3 files changed, 80 insertions, 13 deletions
diff --git a/doc/examples/xml_example.c b/doc/examples/xml_example.c index 3fd77fb..d5fd975 100644 --- a/doc/examples/xml_example.c +++ b/doc/examples/xml_example.c @@ -15,26 +15,35 @@ int main() { xml_open_tag(&ctx, "head"); xml_empty_tag(&ctx, "meta", 1, "charset", "utf-8"); xml_open_tag(&ctx, "title"); - xml_raw(&ctx, "lol this is my site"); + xml_escaped(&ctx, "lol this is my site"); xml_close_including(&ctx, "head"); xml_open_tag(&ctx, "body"); xml_open_tag(&ctx, "header"); xml_open_tag(&ctx, "h1"); - xml_raw(&ctx, "sophisticated technology"); + xml_escaped(&ctx, ">>sophisticated<< technology"); xml_close_including(&ctx, "header"); xml_open_tag(&ctx, "main"); - xml_open_tag(&ctx, "article"); + xml_open_tag_attrs(&ctx, "article", 1, "id", "article-1"); xml_open_tag(&ctx, "h2"); - xml_raw(&ctx, "i was joking"); + xml_escaped(&ctx, "i was joking"); xml_close_tag(&ctx, "h2"); xml_open_tag(&ctx, "p"); - xml_raw(&ctx, "it really was only a joke, i was leading you on!"); + xml_escaped(&ctx, "it really was only a joke, i was leading you on!"); xml_close_including(&ctx, "article"); - xml_open_tag(&ctx, "article"); + xml_open_tag_attrs(&ctx, "article", 2, "id", "article-2", "data-meta", "{ \"type\" : \"preformatted\" }"); + xml_open_tag(&ctx, "h2"); + xml_escaped(&ctx, "table test"); + xml_close_tag(&ctx, "h2"); xml_open_tag(&ctx, "pre"); - xml_raw(&ctx, "+-----+--------------------+\n"); - xml_raw(&ctx, "| wow | this aligns right! |\n"); - xml_raw(&ctx, "+-----+--------------------+\n"); + xml_escaped(&ctx, "+-----+--------------------+\n"); + xml_escaped(&ctx, "| wow | this aligns right! |\n"); + xml_escaped(&ctx, "+-----+--------------------+\n"); + xml_close_including(&ctx, "article"); + xml_open_tag_attrs(&ctx, "article", 1, "id", "article-3"); + xml_open_tag(&ctx, "h2"); + xml_escaped(&ctx, "escaping \"test\""); + xml_close_tag(&ctx, "h2"); + xml_escaped(&ctx, "&<>\"\'"); xml_close_all(&ctx); del_xml_context(&ctx); diff --git a/xml.c b/xml.c index 718351c..5337917 100644 --- a/xml.c +++ b/xml.c @@ -50,6 +50,35 @@ void del_xml_context(struct xml_context *ctx) { } } +void output_xml_escaped_char(FILE *out, char c) { + switch(c) { + case '&': + fputs("&", out); + break; + case '<': + fputs("<", out); + break; + case '>': + fputs(">", out); + break; + case '\'': + fputs("'", out); + break; + case '\"': + fputs(""", out); + break; + default: + fputc(c, out); + break; + } +} + +void xml_escaped(struct xml_context *ctx, const char *str) { + for(size_t i = 0; str[i] != '\0'; i++) { + output_xml_escaped_char(ctx->out, str[i]); + } +} + void xml_raw(struct xml_context *ctx, const char *str) { fputs(str, ctx->out); } @@ -69,7 +98,9 @@ void output_attrs(FILE *out, va_list attrs, size_t arg_count) { char *maybe_val = va_arg(attrs, char *); if(maybe_val != NULL) { fputs("=\"", out); - fputs(maybe_val, out); + for(size_t i = 0; maybe_val[i] != '\0'; i++) { + output_xml_escaped_char(out, maybe_val[i]); + } fputc('\"', out); } } diff --git a/xml.h b/xml.h index c4bfb9f..be43faa 100644 --- a/xml.h +++ b/xml.h @@ -17,8 +17,6 @@ * Currently it has some limitations: * * * It does not give the calling code feedback if errors occurred - * * It can't do any type of escaping (neither in attributes nor - * in content of XML tags) * * It doesn't do validity checking of tags and attributes * (legal characters etc.) * * It can't generate pretty output (i. e. properly indented), @@ -108,12 +106,38 @@ void new_xml_context(struct xml_context *ctx); void del_xml_context(struct xml_context *ctx); /*! + * @brief Output a xml escaped string + * + * Outputs the given string escaped for use with XML. It only + * does minimal-ish escaping, i. e. it escapes all characters + * that have some syntactical meaning in XML. That includes: + * Angled brackets (lower than and greater than), ampersand, + * and single as well as double quotes. All other characters + * are passed through as is and the caller is expected to + * make sure they are correctly encoded, i. e. valid UTF-8 + * characters. + * + * The escaping is not as minimal as possible. In some cases + * you can omit escaping all characters except for `<` and `&`, + * but this would be context-sensitive and therefore + * unnecessarily tedious to implement. Escaping all + * syntactically significant characters has no real downsides + * except maybe using a tiny bit more storage than absolutely + * necessary. + * + * @see xml_raw + */ +void xml_escaped(struct xml_context *ctx, const char *str); + +/*! * @brief Output a raw string. * * Output string to `ctx->out`, equivalent to `fputs(str, ctx.out)`. - * Note that any escaping necessary must be done manually. + * If your string is not already XML, use xml_escaped() to output it + * correcty escaped. * * @see struct xml_context + * @see xml_escaped */ void xml_raw(struct xml_context *ctx, const char *str); @@ -130,6 +154,9 @@ void xml_raw(struct xml_context *ctx, const char *str); * For example, `xml_empty_tag(&ctx, "my-tag", 2, "foo", "bar", "baz", NULL);` gives * `<my-tag foo="bar" baz/>` with default settings. * + * The attributes' values are XML-escaped automatically. For details on how escaping + * works in xml.h, see xml_escaped(). + * * If `closing_slash` is 0 in `ctx`, the slash before the closing ">" will be omitted. * This is useful for HTML5 where it is optional. * |