// file : cli/context.cxx
// author : Boris Kolpackov ";
break;
}
}
// Output paragraph text.
//
if (n != 0)
format_line (ot, r, l, n);
// Output intermediate markup, if any.
//
if (ot == ot_html)
{
switch (b)
{
case block::li:
if (blocks.top () == block::dl)
r += "";
if (b & 2)
r += "";
if (b & 4)
r += "";
break;
}
case ot_man:
{
if ((b & 6) == 6)
r += "\\f(BI";
else if (b & 2)
r += "\\fI";
else if (b & 4)
r += "\\fB";
break;
}
}
}
escape = false;
}
else // Not escape.
{
switch (c)
{
case '\\':
{
escape = true;
break;
}
case '.':
{
if (ot == ot_man)
r += "\\.";
else
r += '.';
break;
}
case '}':
{
if (!blocks.empty ())
{
unsigned char b (blocks.top ());
switch (ot)
{
case ot_plain:
{
if (b & 1)
r += "'";
break;
}
case ot_html:
{
if (b & 4)
r += "";
if (b & 2)
r += "";
if (b & 1)
r += "
";
break;
}
case ot_man:
{
if (b & 6)
r += "\\fP";
break;
}
}
blocks.pop ();
break;
}
// Fall through.
}
default:
r += c;
break;
}
}
}
if (escape)
{
cerr << "error: unterminated escape sequence in documentation "
<< "paragraph '" << string (s, 0, n) << "'" << endl;
throw generation_failed ();
}
if (!blocks.empty ())
{
unsigned char b (blocks.top ());
string bs;
if (b & 1) bs += 'c';
if (b & 2) bs += 'i';
if (b & 4) bs += 'b';
cerr << "error: unterminated formatting block '\\" << bs << "' "
<< "in documentation paragraph '" << string (s, 0, n) << "'" << endl;
throw generation_failed ();
}
}
struct block
{
enum value {h, ul, ol, dl, li, text} v_;
block (value v = text): v_ (v) {}
operator value () const {return v_;}
};
const char* block_str[] = {"\\h", "\\ul", "\\ol", "\\dl", "\\li", "text"};
inline ostream&
operator<< (ostream& os, block b)
{
return os << block_str[b];
}
string context::
format (output_type ot, string const& s, bool first_para)
{
string r;
stack";
r.append (l, n);
if (ot == ot_html)
r += "
";
para = true;
}
else
{
const char* ol (l); // Original, full line, for diagnostics.
size_t on (n);
// First determine what kind of paragraph block this is (i.e.,
// handle the "prefix").
//
block b (block::text);
if (n >= 3 && strncmp (l, "\\h|", 3) == 0)
{
b = block::h;
l += 3;
n -= 3;
}
else if (n >= 4 &&
(strncmp (l, "\\ul|", 4) == 0 ||
strncmp (l, "\\ol|", 4) == 0 ||
strncmp (l, "\\dl|", 4) == 0))
{
switch (l[1])
{
case 'u': b = block::ul; break;
case 'o': b = block::ol; break;
case 'd': b = block::dl; break;
}
l += 4;
n -= 4;
}
else if (n >= 4 && strncmp (l, "\\li|", 4) == 0)
{
b = block::li;
l += 4;
n -= 4;
}
// Skip leading spaces after opening '|'.
//
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 (i.e., handle the "suffix"). Things get a bit complicated
// since '|' could be escaped.
//
size_t pc (0); // Pop count.
for (; n - pc > 0 && l[n - pc - 1] == '|'; ++pc) ;
if (pc != 0)
{
// To determine whether the first '|' is part of an escape sequence
// we have to find the first non-backslash character and then figure
// out who escapes whom.
//
size_t ec (0); // Escape count.
for (; n - pc - ec > 0 && l[n - pc - ec - 1] == '\\'; ++ec) ;
// If we have an odd number of backslashes, then the last '|' is
// escaped.
//
if (ec % 2 != 0)
--pc;
n -= pc; // Number of special '|' at the end.
// Skip trailing spaces before closing '|'.
//
while (n != 0 && (l[n - 1] == 0x20 ||
l[n - 1] == 0x0D ||
l[n - 1] == 0x09))
n--;
}
if (pc > blocks.size () + (b != block::text ? 1 : 0))
{
cerr << "error: extraneous '|' at the end of paragraph '"
<< string (ol, 0, on) << "'" << endl;
throw generation_failed ();
}
// Verify that this block type is valid in this context. Skip
// empty text blocks (can happen if we just have '|').
//
if (b != block::text || n != 0)
{
bool good (true);
block ob (blocks.empty () ? block (block::text) : blocks.top ());
switch (ob)
{
case block::h: good = false; break;
case block::ul:
case block::ol:
case block::dl: good = (b == block::li); break;
case block::li: good = (b == block::text); break;
case block::text: good = (b != block::li); break;
}
if (!good)
{
cerr << "error: " << b << " inside " << ob << " "
<< "in documentation string '" << s << "'" << endl;
throw generation_failed ();
}
}
// Verify the block itself.
//
switch (b)
{
case block::h:
// \h blocks are only valid if we are required to start a new
// paragraph (first_para is true).
//
if (!first_para)
{
cerr << "error: paragraph '" << string (ol, 0, on) << "' "
<< "not allowed in '" << s << "'" << endl;
throw generation_failed ();
}
// \h must be single-paragraph.
//
if (pc == 0)
{
cerr << "error: '|' expected at the end of paragraph '"
<< string (ol, 0, on) << "'" << endl;
throw generation_failed ();
}
// \h must not be empty.
//
if (n == 0)
{
cerr << "error: empty paragraph '" << string (ol, 0, on) << "' "
<< "in documentation string '" << s << "'" << endl;
throw generation_failed ();
}
break;
case block::ul:
case block::ol:
case block::dl:
if (pc != 0)
{
cerr << "error: empty list '" << string (ol, 0, on) << "' "
<< "in documentation string '" << s << "'" << endl;
throw generation_failed ();
}
if (n != 0)
{
cerr << "error: unexpected text after " << b << "| "
<< "in paragraph '" << string (ol, 0, on) << "'" << endl;
throw generation_failed ();
}
break;
case block::li:
if (blocks.top () == block::dl)
{
if (n == 0)
{
cerr << "error: term text missing in paragraph '"
<< string (ol, 0, on) << "'" << endl;
throw generation_failed ();
}
}
break;
case block::text:
break;
}
// Output opening markup.
//
if (ot == ot_html)
{
switch (b)
{
case block::h: r += ""; break;
case block::ul: r += "
"; break;
case block::ol: r += "
"; break;
case block::dl: r += "
"; break;
case block::li:
r += (blocks.top () == block::dl ? "