+ char *c, *ws, *end;
+ struct mdoc_node *n;
+
+ /* No text before an initial macro. */
+
+ if (SEC_NONE == mdoc->lastnamed) {
+ mdoc_pmsg(mdoc, line, offs, MANDOCERR_NOTEXT);
+ return(1);
+ }
+
+ assert(mdoc->last);
+ n = mdoc->last;
+
+ /*
+ * Divert directly to list processing if we're encountering a
+ * columnar MDOC_BLOCK with or without a prior MDOC_BLOCK entry
+ * (a MDOC_BODY means it's already open, in which case we should
+ * process within its context in the normal way).
+ */
+
+ if (MDOC_Bl == n->tok && MDOC_BODY == n->type &&
+ LIST_column == n->norm->Bl.type) {
+ /* `Bl' is open without any children. */
+ mdoc->flags |= MDOC_FREECOL;
+ return(mdoc_macro(mdoc, MDOC_It, line, offs, &offs, buf));
+ }
+
+ if (MDOC_It == n->tok && MDOC_BLOCK == n->type &&
+ NULL != n->parent &&
+ MDOC_Bl == n->parent->tok &&
+ LIST_column == n->parent->norm->Bl.type) {
+ /* `Bl' has block-level `It' children. */
+ mdoc->flags |= MDOC_FREECOL;
+ return(mdoc_macro(mdoc, MDOC_It, line, offs, &offs, buf));
+ }
+
+ /*
+ * Search for the beginning of unescaped trailing whitespace (ws)
+ * and for the first character not to be output (end).
+ */
+
+ /* FIXME: replace with strcspn(). */
+ ws = NULL;
+ for (c = end = buf + offs; *c; c++) {
+ switch (*c) {
+ case ' ':
+ if (NULL == ws)
+ ws = c;
+ continue;
+ case '\t':
+ /*
+ * Always warn about trailing tabs,
+ * even outside literal context,
+ * where they should be put on the next line.
+ */
+ if (NULL == ws)
+ ws = c;
+ /*
+ * Strip trailing tabs in literal context only;
+ * outside, they affect the next line.
+ */
+ if (MDOC_LITERAL & mdoc->flags)
+ continue;
+ break;
+ case '\\':
+ /* Skip the escaped character, too, if any. */
+ if (c[1])
+ c++;
+ /* FALLTHROUGH */
+ default:
+ ws = NULL;
+ break;
+ }
+ end = c + 1;
+ }
+ *end = '\0';