diff --git a/lib/rst2rfcxml.cpp b/lib/rst2rfcxml.cpp
index 49dbb4d..8c3667b 100644
--- a/lib/rst2rfcxml.cpp
+++ b/lib/rst2rfcxml.cpp
@@ -303,6 +303,33 @@ _replace_all(string line, string from, string to)
return line;
}
+string
+rst2rfcxml::define_anchor(string value)
+{
+ string anchor;
+ if (_anchors.find(value) == _anchors.end()) {
+ // Create a new anchor.
+ anchor = _anchor(value);
+ } else {
+ // This is a duplicate anchor definition so create a new one and
+ // map all future lookups to this latest one.
+ anchor = _anchors[value] + "-";
+ }
+ _anchors[value] = anchor;
+ return anchor;
+ }
+
+string
+rst2rfcxml::lookup_anchor(string value)
+{
+ if (_anchors.find(value) != _anchors.end()) {
+ return _anchors[value];
+ }
+
+ // Undefined anchor.
+ return _anchor(value);
+}
+
string
rst2rfcxml::replace_term_links(string line)
{
@@ -325,7 +352,7 @@ rst2rfcxml::replace_term_links(string line)
term = middle.substr(label_end + 4, term_end - label_end - 4);
}
- line = fmt::format("{}{}{}", before, _anchor(term), label, after);
+ line = fmt::format("{}{}{}", before, lookup_anchor("term-" + term), label, after);
}
return line;
}
@@ -425,7 +452,7 @@ rst2rfcxml::replace_reference_links(string line)
}
}
- line = fmt::format("{}{}{}", before, _anchor(middle), middle, after);
+ line = fmt::format("{}{}{}", before, lookup_anchor(middle), middle, after);
}
return line;
}
@@ -802,11 +829,14 @@ rst2rfcxml::handle_section_title(int level, string marker, string current, strin
push_context(output_stream, xml_context::MIDDLE);
}
string title = handle_escapes_and_links(current);
- push_context(
- output_stream,
- xml_context::SECTION,
- current_indentation,
- fmt::format("anchor=\"{}\" title=\"{}\"", _anchor(title), title));
+ string anchor = define_anchor(title);
+ string attributes;
+ if (anchor.empty()) {
+ attributes = fmt::format("title=\"{}\"", title);
+ } else {
+ attributes = fmt::format("anchor=\"{}\" title=\"{}\"", anchor, title);
+ }
+ push_context(output_stream, xml_context::SECTION, current_indentation, attributes);
return true;
}
if (current.starts_with(marker) && current.find_first_not_of(marker, 0) == string::npos &&
@@ -972,8 +1002,13 @@ rst2rfcxml::process_line(string current, string next, ostream& output_stream)
if (!in_context(xml_context::DEFINITION_LIST)) {
push_context(output_stream, xml_context::DEFINITION_LIST, current_indentation);
}
- string attributes = fmt::format("anchor=\"term-{}\"", _anchor(_trim(current)));
- push_context(output_stream, xml_context::DEFINITION_TERM, current_indentation, attributes);
+ string anchor = define_anchor("term-" + _trim(current));
+ if (anchor.empty()) {
+ push_context(output_stream, xml_context::DEFINITION_TERM, current_indentation);
+ } else {
+ string attributes = fmt::format("anchor=\"{}\"", anchor);
+ push_context(output_stream, xml_context::DEFINITION_TERM, current_indentation, attributes);
+ }
}
// Handle artwork.
diff --git a/lib/rst2rfcxml.h b/lib/rst2rfcxml.h
index 721ee96..d3964b5 100644
--- a/lib/rst2rfcxml.h
+++ b/lib/rst2rfcxml.h
@@ -131,6 +131,10 @@ class rst2rfcxml
std::string
replace_term_links(std::string line);
std::string
+ define_anchor(std::string term);
+ std::string
+ lookup_anchor(std::string term);
+ std::string
handle_escapes_and_links(std::string line);
void
output_table_row(std::ostream& output_stream);
@@ -140,6 +144,7 @@ class rst2rfcxml
std::string _ipr;
std::string _category;
std::vector _column_indices;
+ std::map _anchors;
std::map _authors;
std::string _submission_type;
std::string _abbreviated_title;
diff --git a/test/basic_tests.cpp b/test/basic_tests.cpp
index 332cf84..dd973cd 100644
--- a/test/basic_tests.cpp
+++ b/test/basic_tests.cpp
@@ -9,6 +9,25 @@
using namespace std;
+constexpr const char* BASIC_PREAMBLE = R"(
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+)";
+
void
test_rst2rfcxml(const char* input, const char* expected_output)
{
@@ -99,6 +118,40 @@ TEST_CASE("titles", "[basic]")
test_rst2rfcxml("Foo\n~~~\n", "\n");
}
+TEST_CASE("duplicate titles", "[basic]")
+{
+ test_rst2rfcxml(
+ R"(.. header::
+
+foo
+===
+
+bar
+
+foo
+===
+
+baz
+)", (std::string(BASIC_PREAMBLE) + R"(
+
+
+
+
+
+
+
+
+)").c_str());
+}
+
TEST_CASE("line block", "[basic]")
{
test_rst2rfcxml(
@@ -315,6 +368,101 @@ baz :term:`foo-ish` baz
)");
}
+TEST_CASE("definition list duplication", "[basic]")
+{
+ test_rst2rfcxml(
+ R"(
+foo
+ description
+
+bar
+ description
+
+baz :term:`foo-ish` baz
+
+foo
+ description 2
+
+bar
+ description 2
+
+baz :term:`foo-ish` baz
+
+foo
+ description 3
+
+bar
+ description 3
+
+baz :term:`foo-ish` baz
+
+)",
+ R"(
+ -
+ foo
+
+ -
+
+ description
+
+
+ -
+ bar
+
+ -
+
+ description
+
+
+
+
+ baz foo-ish baz
+
+
+ -
+ foo
+
+ -
+
+ description 2
+
+
+ -
+ bar
+
+ -
+
+ description 2
+
+
+
+
+ baz foo-ish baz
+
+
+ -
+ foo
+
+ -
+
+ description 3
+
+
+ -
+ bar
+
+ -
+
+ description 3
+
+
+
+
+ baz foo-ish baz
+
+)");
+}
+
TEST_CASE("definition list with glossary label", "[basic]")
{
test_rst2rfcxml(
@@ -775,25 +923,6 @@ Paragraph two.
)");
}
-constexpr const char* BASIC_PREAMBLE = R"(
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-)";
-
TEST_CASE("empty header", "[basic]")
{
string expected_output = BASIC_PREAMBLE;