+ (void)snprintf(buf, sizeof(buf), "%un", (unsigned int)width);
+ free(n->args->argv[i].value[0]);
+ n->args->argv[i].value[0] = mandoc_strdup(buf);
+
+ /* Set our width! */
+ n->norm->Bl.width = n->args->argv[i].value[0];
+ return(1);
+}
+
+static int
+post_bl_block_tag(POST_ARGS)
+{
+ struct mdoc_node *n, *nn;
+ size_t sz, ssz;
+ int i;
+ char buf[24];
+
+ /*
+ * Calculate the -width for a `Bl -tag' list if it hasn't been
+ * provided. Uses the first head macro. NOTE AGAIN: this is
+ * ONLY if the -width argument has NOT been provided. See
+ * post_bl_block_width() for converting the -width string.
+ */
+
+ sz = 10;
+ n = mdoc->last;
+
+ for (nn = n->body->child; nn; nn = nn->next) {
+ if (MDOC_It != nn->tok)
+ continue;
+
+ assert(MDOC_BLOCK == nn->type);
+ nn = nn->head->child;
+
+ if (nn == NULL)
+ break;
+
+ if (MDOC_TEXT == nn->type) {
+ sz = strlen(nn->string) + 1;
+ break;
+ }
+
+ if (0 != (ssz = macro2len(nn->tok)))
+ sz = ssz;
+
+ break;
+ }
+
+ /* Defaults to ten ens. */
+
+ (void)snprintf(buf, sizeof(buf), "%un", (unsigned int)sz);
+
+ /*
+ * We have to dynamically add this to the macro's argument list.
+ * We're guaranteed that a MDOC_Width doesn't already exist.
+ */
+
+ assert(n->args);
+ i = (int)(n->args->argc)++;
+
+ n->args->argv = mandoc_reallocarray(n->args->argv,
+ n->args->argc, sizeof(struct mdoc_argv));
+
+ n->args->argv[i].arg = MDOC_Width;
+ n->args->argv[i].line = n->line;
+ n->args->argv[i].pos = n->pos;
+ n->args->argv[i].sz = 1;
+ n->args->argv[i].value = mandoc_malloc(sizeof(char *));
+ n->args->argv[i].value[0] = mandoc_strdup(buf);
+
+ /* Set our width! */
+ n->norm->Bl.width = n->args->argv[i].value[0];
+ return(1);
+}
+
+static int
+post_bl_head(POST_ARGS)
+{
+ struct mdoc_node *np, *nn, *nnp;
+ int i, j;
+
+ if (LIST_column != mdoc->last->norm->Bl.type)
+ /* FIXME: this should be ERROR class... */
+ return(hwarn_eq0(mdoc));
+
+ /*
+ * Convert old-style lists, where the column width specifiers
+ * trail as macro parameters, to the new-style ("normal-form")
+ * lists where they're argument values following -column.
+ */
+
+ /* First, disallow both types and allow normal-form. */
+
+ /*
+ * TODO: technically, we can accept both and just merge the two
+ * lists, but I'll leave that for another day.
+ */
+
+ if (mdoc->last->norm->Bl.ncols && mdoc->last->nchild) {
+ mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_COLUMNS);
+ return(0);
+ } else if (NULL == mdoc->last->child)
+ return(1);
+
+ np = mdoc->last->parent;
+ assert(np->args);
+
+ for (j = 0; j < (int)np->args->argc; j++)
+ if (MDOC_Column == np->args->argv[j].arg)
+ break;
+
+ assert(j < (int)np->args->argc);
+ assert(0 == np->args->argv[j].sz);
+
+ /*
+ * Accommodate for new-style groff column syntax. Shuffle the
+ * child nodes, all of which must be TEXT, as arguments for the
+ * column field. Then, delete the head children.
+ */
+
+ np->args->argv[j].sz = (size_t)mdoc->last->nchild;
+ np->args->argv[j].value = mandoc_reallocarray(NULL,
+ (size_t)mdoc->last->nchild, sizeof(char *));
+
+ mdoc->last->norm->Bl.ncols = np->args->argv[j].sz;
+ mdoc->last->norm->Bl.cols = (void *)np->args->argv[j].value;
+
+ for (i = 0, nn = mdoc->last->child; nn; i++) {
+ np->args->argv[j].value[i] = nn->string;
+ nn->string = NULL;
+ nnp = nn;
+ nn = nn->next;
+ mdoc_node_delete(NULL, nnp);
+ }
+
+ mdoc->last->nchild = 0;
+ mdoc->last->child = NULL;
+
+ return(1);
+}
+
+static int
+post_bl(POST_ARGS)
+{
+ struct mdoc_node *nparent, *nprev; /* of the Bl block */
+ struct mdoc_node *nblock, *nbody; /* of the Bl */
+ struct mdoc_node *nchild, *nnext; /* of the Bl body */
+
+ nbody = mdoc->last;
+ switch (nbody->type) {
+ case MDOC_BLOCK:
+ return(post_bl_block(mdoc));
+ case MDOC_HEAD:
+ return(post_bl_head(mdoc));
+ case MDOC_BODY:
+ break;
+ default:
+ return(1);
+ }
+
+ nchild = nbody->child;
+ while (NULL != nchild) {
+ if (MDOC_It == nchild->tok || MDOC_Sm == nchild->tok) {
+ nchild = nchild->next;
+ continue;
+ }
+
+ mdoc_nmsg(mdoc, nchild, MANDOCERR_CHILD);
+
+ /*
+ * Move the node out of the Bl block.
+ * First, collect all required node pointers.
+ */
+
+ nblock = nbody->parent;
+ nprev = nblock->prev;
+ nparent = nblock->parent;
+ nnext = nchild->next;
+
+ /*
+ * Unlink this child.
+ */
+
+ assert(NULL == nchild->prev);
+ if (0 == --nbody->nchild) {
+ nbody->child = NULL;
+ nbody->last = NULL;
+ assert(NULL == nnext);
+ } else {
+ nbody->child = nnext;
+ nnext->prev = NULL;
+ }
+
+ /*
+ * Relink this child.
+ */
+
+ nchild->parent = nparent;
+ nchild->prev = nprev;
+ nchild->next = nblock;
+
+ nblock->prev = nchild;
+ nparent->nchild++;
+ if (NULL == nprev)
+ nparent->child = nchild;
+ else
+ nprev->next = nchild;
+
+ nchild = nnext;
+ }
+
+ return(1);
+}
+
+static int
+ebool(struct mdoc *mdoc)
+{
+
+ if (NULL == mdoc->last->child) {
+ mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
+ mdoc_node_delete(mdoc, mdoc->last);
+ return(1);
+ }
+ check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
+
+ assert(MDOC_TEXT == mdoc->last->child->type);
+
+ if (0 == strcmp(mdoc->last->child->string, "on")) {
+ if (MDOC_Sm == mdoc->last->tok)
+ mdoc->flags &= ~MDOC_SMOFF;
+ return(1);
+ }
+ if (0 == strcmp(mdoc->last->child->string, "off")) {
+ if (MDOC_Sm == mdoc->last->tok)
+ mdoc->flags |= MDOC_SMOFF;
+ return(1);
+ }
+
+ mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADBOOL);
+ return(1);
+}