From 5a867cb1e38960559b0636bbdc8ea57af151b1ea Mon Sep 17 00:00:00 2001 From: m-naumann Date: Tue, 18 Jun 2019 19:51:10 -0700 Subject: [PATCH] Add support for using single quotes to enclose attribute values This change adds format_attribute_single_quote flag that uses single quotes (`'`) instead of double quotes (`"`) for formatting attribute values. Internal quotation marks are escaped using `"` and `'`. --- docs/manual.adoc | 2 ++ docs/manual.html | 5 ++++- src/pugixml.cpp | 13 +++++++++---- src/pugixml.hpp | 3 +++ tests/test_write.cpp | 6 +++--- 5 files changed, 21 insertions(+), 8 deletions(-) diff --git a/docs/manual.adoc b/docs/manual.adoc index 6294606..7cb2466 100644 --- a/docs/manual.adoc +++ b/docs/manual.adoc @@ -1706,6 +1706,8 @@ These flags control the resulting tree contents: * [[format_skip_control_chars]]`format_skip_control_chars` enables skipping characters belonging to range [0; 32) instead of "&#xNN;" encoding. This flag is *off* by default. +* [[format_attribute_single_quote]]`format_attribute_single_quote` enables using single quotes `'` instead of double quotes `"` for enclosing attribute values. This flag is *off* by default. + These flags control the additional output information: * [[format_no_declaration]]`format_no_declaration` disables default node declaration output. By default, if the document is saved via `save` or `save_file` function, and it does not have any document declaration, a default declaration is output before the document contents. Enabling this flag disables this declaration. This flag has no effect in `xml_node::print` functions: they never output the default declaration. This flag is *off* by default. diff --git a/docs/manual.html b/docs/manual.html index 8620392..c697942 100644 --- a/docs/manual.html +++ b/docs/manual.html @@ -3327,6 +3327,9 @@ You should use the usual bitwise arithmetics to manipulate the bitmask: to enabl
  • format_skip_control_chars enables skipping characters belonging to range [0; 32) instead of "&#xNN;" encoding. This flag is off by default.

  • +
  • +

    format_attribute_single_quote enables using single quotes ' instead of double quotes " for enclosing attribute values. This flag is off by default.

    +
  • @@ -5775,4 +5778,4 @@ Last updated 2019-03-09 06:55:22 STD
    - \ No newline at end of file + diff --git a/src/pugixml.cpp b/src/pugixml.cpp index 37b3f95..87e58fb 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -1861,7 +1861,7 @@ PUGI__NS_BEGIN enum chartypex_t { ctx_special_pcdata = 1, // Any symbol >= 0 and < 32 (except \t, \r, \n), &, <, > - ctx_special_attr = 2, // Any symbol >= 0 and < 32, &, <, >, " + ctx_special_attr = 2, // Any symbol >= 0 and < 32, &, <, >, ", ' ctx_start_symbol = 4, // Any symbol > 127, a-z, A-Z, _ ctx_digit = 8, // 0-9 ctx_symbol = 16 // Any symbol > 127, a-z, A-Z, 0-9, _, -, . @@ -1871,7 +1871,7 @@ PUGI__NS_BEGIN { 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 3, 3, 2, 3, 3, // 0-15 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 16-31 - 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 16, 16, 0, // 32-47 + 0, 0, 2, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0, 16, 16, 0, // 32-47 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 3, 0, 3, 0, // 48-63 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 64-79 @@ -3933,6 +3933,10 @@ PUGI__NS_BEGIN writer.write('&', 'q', 'u', 'o', 't', ';'); ++s; break; + case '\'': + writer.write('&', 'a', 'p', 'o', 's', ';'); + ++s; + break; default: // s is not a usual symbol { unsigned int ch = static_cast(*s++); @@ -4064,6 +4068,7 @@ PUGI__NS_BEGIN PUGI__FN void node_output_attributes(xml_buffered_writer& writer, xml_node_struct* node, const char_t* indent, size_t indent_length, unsigned int flags, unsigned int depth) { const char_t* default_name = PUGIXML_TEXT(":anonymous"); + const char_t enquotation_char = (flags & format_attribute_single_quote) ? '\'' : '"'; for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute) { @@ -4079,12 +4084,12 @@ PUGI__NS_BEGIN } writer.write_string(a->name ? a->name + 0 : default_name); - writer.write('=', '"'); + writer.write('=', enquotation_char); if (a->value) text_output(writer, a->value, ctx_special_attr, flags); - writer.write('"'); + writer.write(enquotation_char); } } diff --git a/src/pugixml.hpp b/src/pugixml.hpp index d01f15f..972f8a3 100644 --- a/src/pugixml.hpp +++ b/src/pugixml.hpp @@ -255,6 +255,9 @@ namespace pugi // Skip characters belonging to range [0; 32) instead of "&#xNN;" encoding. This flag is off by default. const unsigned int format_skip_control_chars = 0x100; + // Use single quotes ' instead of double quotes " for enclosing attribute values. This flag is off by default. + const unsigned int format_attribute_single_quote = 0x200; + // The default set of formatting flags. // Nodes are indented depending on their depth in DOM tree, a default declaration is output if document has none. const unsigned int format_default = format_indent; diff --git a/tests/test_write.cpp b/tests/test_write.cpp index 9050807..9a0ed06 100644 --- a/tests/test_write.cpp +++ b/tests/test_write.cpp @@ -193,7 +193,7 @@ TEST_XML(write_escape, "text") doc.child(STR("node")).attribute(STR("attr")) = STR("<>'\"&\x04\r\n\t"); doc.child(STR("node")).first_child().set_value(STR("<>'\"&\x04\r\n\t")); - CHECK_NODE(doc, STR("<>'\"&\r\n\t")); + CHECK_NODE(doc, STR("<>'\"&\r\n\t")); } TEST_XML(write_escape_roundtrip, "text") @@ -207,7 +207,7 @@ TEST_XML(write_escape_roundtrip, "text") // Note: this string is almost identical to the string from write_escape with the exception of \r // \r in PCDATA doesn't roundtrip because it has to go through newline conversion (which could be disabled, but is active by default) - CHECK_NODE(doc, STR("<>'\"&\n\t")); + CHECK_NODE(doc, STR("<>'\"&\n\t")); } TEST_XML(write_escape_unicode, "") @@ -632,7 +632,7 @@ TEST_XML(write_no_empty_element_tags, "textpremid<text&escapepostfin", parse_full) { - const unsigned int flagset[] = { format_indent, format_raw, format_no_declaration, format_indent_attributes, format_no_empty_element_tags }; + const unsigned int flagset[] = { format_indent, format_raw, format_no_declaration, format_indent_attributes, format_no_empty_element_tags, format_attribute_single_quote }; size_t flagcount = sizeof(flagset) / sizeof(flagset[0]); for (size_t i = 0; i < (size_t(1) << flagcount); ++i)