about summary refs log tree commit diff
diff options
context:
space:
mode:
authorsternenseemann <sternenseemann@systemli.org>2020-08-11 13:41:33 +0200
committersternenseemann <sternenseemann@systemli.org>2020-08-11 13:41:33 +0200
commitf863b428f301e4b549308c06f2cf14c6ad287fb7 (patch)
tree017fc869f75baa771b1798036b14c0f252636d6f
parentc82544f10ccf6a916f29d4a704c55f1eb9b12b62 (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.c27
-rw-r--r--xml.c33
-rw-r--r--xml.h33
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("&amp;", out);
+            break;
+        case '<':
+            fputs("&lt;", out);
+            break;
+        case '>':
+            fputs("&gt;", out);
+            break;
+        case '\'':
+            fputs("&apos;", out);
+            break;
+        case '\"':
+            fputs("&quot;", 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.
  *