summaryrefslogtreecommitdiff
path: root/cli
diff options
context:
space:
mode:
Diffstat (limited to 'cli')
-rw-r--r--cli/context.cxx137
-rw-r--r--cli/context.hxx14
-rw-r--r--cli/generator.cxx6
-rw-r--r--cli/source.cxx10
4 files changed, 148 insertions, 19 deletions
diff --git a/cli/context.cxx b/cli/context.cxx
index ce58779..b923c4b 100644
--- a/cli/context.cxx
+++ b/cli/context.cxx
@@ -115,6 +115,8 @@ context (ostream& os_,
reserved_name_map (options.reserved_name ()),
keyword_set (data_->keyword_set_),
link_regex (data_->link_regex_),
+ id_set (data_->id_set_),
+ ref_set (data_->ref_set_),
toc (data_->toc_),
tocs (data_->tocs_)
{
@@ -175,6 +177,8 @@ context (context& c)
reserved_name_map (c.reserved_name_map),
keyword_set (c.keyword_set),
link_regex (c.link_regex),
+ id_set (c.id_set),
+ ref_set (c.ref_set),
toc (c.toc),
tocs (c.tocs)
{
@@ -684,6 +688,15 @@ format_line (output_type ot, string& r, const char* s, size_t n)
else
link_section.clear ();
+ // If this is a local fragment reference, add it to the set to be
+ // verified at the end.
+ //
+ if (t[0] == '#')
+ {
+ assert (link_section.empty ()); // Not a man page.
+ ref_set.insert (string (t, 1, string::npos));
+ }
+
link_empty = i + 1 < n && s[i + 1] == '}';
blocks.push_back (link);
@@ -1063,12 +1076,13 @@ struct block
kind_type kind;
bool para; // True if first text fragment should be in own paragraph.
+ string id;
string header;
string value;
string trailer;
- block (kind_type k, bool p, const string& h = "")
- : kind (k), para (p), header (h) {}
+ block (kind_type k, bool p, const string& i, const string& h = "")
+ : kind (k), para (p), id (i), header (h) {}
};
static const char* block_kind_str[] = {
@@ -1132,7 +1146,7 @@ string context::
format (semantics::scope& scope, string const& s, bool para)
{
stack<block> blocks;
- blocks.push (block (block::text, para)); // Top-level.
+ blocks.push (block (block::text, para, "")); // Top-level.
// Number of li in ol. Since we don't support nested lists, we don't
// need to push it into the stack.
@@ -1235,6 +1249,7 @@ format (semantics::scope& scope, string const& s, bool para)
// First determine what kind of paragraph block this is.
//
block::kind_type k;
+ string id;
string header;
string trailer;
@@ -1246,15 +1261,25 @@ format (semantics::scope& scope, string const& s, bool para)
}
else
{
+ // \h \H
+ //
if (n >= 3 &&
- l[0] == '\\' && (l[1] == 'h' || l[1] == 'H') && l[2] == '|')
+ l[0] == '\\' &&
+ (l[1] == 'h' || l[1] == 'H') &&
+ (l[2] == '|' || l[2] == '#'))
{
k = block::h;
header = l[1];
l += 3;
n -= 3;
}
- else if (n >= 4 && l[0] == '\\' && l[1] == 'h' && l[3] == '|')
+ //
+ // \h0 \h1 \h2
+ //
+ else if (n >= 4 &&
+ l[0] == '\\' &&
+ l[1] == 'h' &&
+ (l[3] == '|' || l[3] == '#'))
{
if (l[2] != '0' && l[2] != '1' && l[2] != '2')
{
@@ -1268,10 +1293,14 @@ format (semantics::scope& scope, string const& s, bool para)
l += 4;
n -= 4;
}
+ //
+ // \ul \ol \dl
+ //
else if (n >= 4 &&
- (strncmp (l, "\\ul|", 4) == 0 ||
- strncmp (l, "\\ol|", 4) == 0 ||
- strncmp (l, "\\dl|", 4) == 0))
+ l[0] == '\\' &&
+ (l[1] == 'u' || l[1] == 'o' || l[1] == 'd') &&
+ l[2] == 'l' &&
+ (l[3] == '|' || l[3] == '#'))
{
switch (l[1])
{
@@ -1283,7 +1312,14 @@ format (semantics::scope& scope, string const& s, bool para)
l += 4;
n -= 4;
}
- else if (n >= 4 && strncmp (l, "\\li|", 4) == 0)
+ //
+ // \li
+ //
+ else if (n >= 4 &&
+ l[0] == '\\' &&
+ l[1] == 'l' &&
+ l[2] == 'i' &&
+ (l[3] == '|' || l[3] == '#'))
{
k = block::li;
l += 4;
@@ -1292,10 +1328,27 @@ format (semantics::scope& scope, string const& s, bool para)
else
k = block::text;
+ // Get the id, if present.
+ //
+ if (k != block::text && *(l - 1) == '#')
+ {
+ for (; n != 0 && *l != '|'; ++l, --n)
+ id += *l;
+
+ if (n == 0)
+ {
+ cerr << "error: paragraph begin '|' expected after id in '"
+ << string (ol, 0, on) << "'" << endl;
+ throw generation_failed ();
+ }
+
+ ++l; --n; // Skip '|'.
+ }
+
// Skip leading spaces after opening '|'.
//
if (k != block::text)
- while (n != 0 && (*l == 0x20 || *l == 0x0D || *l == 0x09)) {l++; n--;}
+ while (n != 0 && (*l == 0x20 || *l == 0x0D || *l == 0x09)) {++l; --n;}
// Next figure out how many blocks we need to pop at the end of this
// paragraph. Things get a bit complicated since '|' could be escaped.
@@ -1356,6 +1409,18 @@ format (semantics::scope& scope, string const& s, bool para)
}
}
+ // Check id for duplicates. Do it only on the non-TOC pass.
+ //
+ if (!toc && !id.empty ())
+ {
+ if (!id_set.insert (id).second)
+ {
+ cerr << "error: duplicate id '" << id << "' in documentation "
+ << "string '" << s << "'" << endl;
+ throw generation_failed ();
+ }
+ }
+
// Verify the block itself.
//
switch (k)
@@ -1435,10 +1500,10 @@ format (semantics::scope& scope, string const& s, bool para)
//
switch (k)
{
- case block::h: blocks.push (block (k, false, header)); break;
+ case block::h: blocks.push (block (k, false, id, header)); break;
case block::ul:
case block::ol: ol_count = 0; // Fall through.
- case block::dl: blocks.push (block (k, true)); break;
+ case block::dl: blocks.push (block (k, true, id)); break;
case block::li:
{
switch (blocks.top ().kind)
@@ -1460,7 +1525,7 @@ format (semantics::scope& scope, string const& s, bool para)
break;
}
- blocks.push (block (k, false, header));
+ blocks.push (block (k, false, id, header));
break;
}
case block::text: break; // No push.
@@ -1609,8 +1674,9 @@ format (semantics::scope& scope, string const& s, bool para)
for (; pop != 0; --pop)
{
block pb (blocks.top ()); // move
- string& pv (pb.value);
+ string& pi (pb.id);
string& ph (pb.header);
+ string& pv (pb.value);
blocks.pop ();
@@ -1825,15 +1891,25 @@ format (semantics::scope& scope, string const& s, bool para)
{
case block::h:
{
+ string h;
+ string c;
+
switch (ph[0])
{
- case '0': v += "<h1 class=\"preface\">" + pv + "</h1>"; break;
- case 'H': v += "<h1 class=\"part\">" + pv + "</h1>"; break;
- case '1': v += "<h1>" + pv + "</h1>"; break;
- case '2': v += "<h3>" + pv + "</h3>"; break;
- case 'h': v += '<' + html_h + '>' + pv + "</" + html_h + '>';
+ case '0': h = "h1"; c = "preface"; break;
+ case 'H': h = "h1"; c = "part"; break;
+ case '1': h = "h1"; break;
+ case 'h': h = html_h; break;
+ case '2': h = "h3"; break;
}
+ v += '<' + h;
+ if (!pi.empty ()) v += " id=\"" + pi + '"';
+ if (!c.empty ()) v += " class=\"" + c + '"';
+ v += '>';
+ v += pv;
+ v += "</" + h + '>';
+
// @@ This only works for a single string fragment.
//
if (ph[0] == '0' || ph[0] == '1')
@@ -1994,6 +2070,29 @@ end_toc ()
}
}
+void context::
+verify_id_ref ()
+{
+ bool f (false);
+
+ for (id_set_type::const_iterator i (ref_set.begin ());
+ i != ref_set.end ();
+ ++i)
+ {
+ if (id_set.find (*i) == id_set.end ())
+ {
+ cerr << "error: no id for fragment link '#" << *i << "'" << endl;
+ f = true;
+ }
+ }
+
+ if (f)
+ throw generation_failed ();
+
+ id_set.clear ();
+ ref_set.clear ();
+}
+
string context::
substitute (const string& s, const path* d)
{
diff --git a/cli/context.hxx b/cli/context.hxx
index 79c9aab..c67cec5 100644
--- a/cli/context.hxx
+++ b/cli/context.hxx
@@ -99,6 +99,11 @@ public:
regex_mapping const& link_regex;
+ typedef std::set<string> id_set_type;
+ id_set_type& id_set;
+ id_set_type& ref_set;
+
+
// TOC phase.
//
// 0 - non-TOC
@@ -126,6 +131,8 @@ private:
string cli_;
keyword_set_type keyword_set_;
regex_mapping link_regex_;
+ id_set_type id_set_;
+ id_set_type ref_set_;
unsigned short toc_;
toc_stack tocs_;
};
@@ -166,6 +173,13 @@ public:
string
end_toc ();
+ // Make sure each local fragment reference has the corresponding id. Issue
+ // diagnostics and throw generation_failed if fails. Otherwise clear the
+ // id and ref sets.
+ //
+ void
+ verify_id_ref ();
+
// Substitute doc variable expansions ($var$). Var must be a C identifier.
// If the path is not NULL, then also recognize names that start with either
// ./ or ../ and treat them as files relative to path. Such file expansions
diff --git a/cli/generator.cxx b/cli/generator.cxx
index cbb3ec7..01f77d5 100644
--- a/cli/generator.cxx
+++ b/cli/generator.cxx
@@ -452,6 +452,8 @@ generate (options const& ops, semantics::cli_unit& unit, path const& p)
ctx.toc++; // TOC phase after restart.
}
}
+
+ ctx.verify_id_ref ();
}
// HTML output
@@ -499,6 +501,8 @@ generate (options const& ops, semantics::cli_unit& unit, path const& p)
ctx.toc++; // TOC phase after restart.
}
}
+
+ ctx.verify_id_ref ();
}
// txt output
@@ -543,6 +547,8 @@ generate (options const& ops, semantics::cli_unit& unit, path const& p)
ctx.toc++; // TOC phase after restart.
}
}
+
+ ctx.verify_id_ref ();
}
auto_rm.cancel ();
diff --git a/cli/source.cxx b/cli/source.cxx
index 57f5922..0184da6 100644
--- a/cli/source.cxx
+++ b/cli/source.cxx
@@ -751,6 +751,8 @@ namespace
len = max;
}
+
+ verify_id_ref ();
}
string up (cli + "::usage_para");
@@ -791,6 +793,8 @@ namespace
os << "return p;"
<< "}";
+ verify_id_ref ();
+
// Long version.
//
if (usage == ut_both)
@@ -826,6 +830,8 @@ namespace
os << "return p;"
<< "}";
+
+ verify_id_ref ();
}
}
@@ -1126,6 +1132,8 @@ generate_source (context& ctx)
os << "return p;"
<< "}";
+
+ ctx.verify_id_ref ();
}
// Long version.
@@ -1166,6 +1174,8 @@ generate_source (context& ctx)
os << "return p;"
<< "}";
+
+ ctx.verify_id_ref ();
}
ctx.ns_close (qn, false);