+void
+roff_word_alloc(struct roff_man *man, int line, int pos, const char *word)
+{
+ struct roff_node *n;
+
+ n = roff_node_alloc(man, line, pos, ROFFT_TEXT, TOKEN_NONE);
+ n->string = roff_strdup(man->roff, word);
+ roff_node_append(man, n);
+ if (man->macroset == MACROSET_MDOC)
+ mdoc_valid_post(man);
+ else
+ man_valid_post(man);
+ man->next = ROFF_NEXT_SIBLING;
+}
+
+void
+roff_word_append(struct roff_man *man, const char *word)
+{
+ struct roff_node *n;
+ char *addstr, *newstr;
+
+ n = man->last;
+ addstr = roff_strdup(man->roff, word);
+ mandoc_asprintf(&newstr, "%s %s", n->string, addstr);
+ free(addstr);
+ free(n->string);
+ n->string = newstr;
+ man->next = ROFF_NEXT_SIBLING;
+}
+
+void
+roff_elem_alloc(struct roff_man *man, int line, int pos, int tok)
+{
+ struct roff_node *n;
+
+ n = roff_node_alloc(man, line, pos, ROFFT_ELEM, tok);
+ roff_node_append(man, n);
+ man->next = ROFF_NEXT_CHILD;
+}
+
+struct roff_node *
+roff_block_alloc(struct roff_man *man, int line, int pos, int tok)
+{
+ struct roff_node *n;
+
+ n = roff_node_alloc(man, line, pos, ROFFT_BLOCK, tok);
+ roff_node_append(man, n);
+ man->next = ROFF_NEXT_CHILD;
+ return n;
+}
+
+struct roff_node *
+roff_head_alloc(struct roff_man *man, int line, int pos, int tok)
+{
+ struct roff_node *n;
+
+ n = roff_node_alloc(man, line, pos, ROFFT_HEAD, tok);
+ roff_node_append(man, n);
+ man->next = ROFF_NEXT_CHILD;
+ return n;
+}
+
+struct roff_node *
+roff_body_alloc(struct roff_man *man, int line, int pos, int tok)
+{
+ struct roff_node *n;
+
+ n = roff_node_alloc(man, line, pos, ROFFT_BODY, tok);
+ roff_node_append(man, n);
+ man->next = ROFF_NEXT_CHILD;
+ return n;
+}
+
+void
+roff_addeqn(struct roff_man *man, const struct eqn *eqn)
+{
+ struct roff_node *n;
+
+ n = roff_node_alloc(man, eqn->ln, eqn->pos, ROFFT_EQN, TOKEN_NONE);
+ n->eqn = eqn;
+ if (eqn->ln > man->last->line)
+ n->flags |= MDOC_LINE;
+ roff_node_append(man, n);
+ man->next = ROFF_NEXT_SIBLING;
+}
+
+void
+roff_addtbl(struct roff_man *man, const struct tbl_span *tbl)
+{
+ struct roff_node *n;
+
+ if (man->macroset == MACROSET_MAN)
+ man_breakscope(man, TOKEN_NONE);
+ n = roff_node_alloc(man, tbl->line, 0, ROFFT_TBL, TOKEN_NONE);
+ n->span = tbl;
+ roff_node_append(man, n);
+ if (man->macroset == MACROSET_MDOC)
+ mdoc_valid_post(man);
+ else
+ man_valid_post(man);
+ man->next = ROFF_NEXT_SIBLING;
+}
+
+void
+roff_node_unlink(struct roff_man *man, struct roff_node *n)
+{
+
+ /* Adjust siblings. */
+
+ if (n->prev)
+ n->prev->next = n->next;
+ if (n->next)
+ n->next->prev = n->prev;
+
+ /* Adjust parent. */
+
+ if (n->parent != NULL) {
+ n->parent->nchild--;
+ if (n->parent->child == n)
+ n->parent->child = n->next;
+ if (n->parent->last == n)
+ n->parent->last = n->prev;
+ }
+
+ /* Adjust parse point. */
+
+ if (man == NULL)
+ return;
+ if (man->last == n) {
+ if (n->prev == NULL) {
+ man->last = n->parent;
+ man->next = ROFF_NEXT_CHILD;
+ } else {
+ man->last = n->prev;
+ man->next = ROFF_NEXT_SIBLING;
+ }
+ }
+ if (man->first == n)
+ man->first = NULL;
+}
+
+void
+roff_node_free(struct roff_node *n)
+{
+
+ if (n->args != NULL)
+ mdoc_argv_free(n->args);
+ if (n->type == ROFFT_BLOCK || n->type == ROFFT_ELEM)
+ free(n->norm);
+ free(n->string);
+ free(n);
+}
+
+void
+roff_node_delete(struct roff_man *man, struct roff_node *n)
+{
+
+ while (n->child != NULL)
+ roff_node_delete(man, n->child);
+ assert(n->nchild == 0);
+ roff_node_unlink(man, n);
+ roff_node_free(n);
+}
+
+void
+deroff(char **dest, const struct roff_node *n)
+{
+ char *cp;
+ size_t sz;
+
+ if (n->type != ROFFT_TEXT) {
+ for (n = n->child; n != NULL; n = n->next)
+ deroff(dest, n);
+ return;
+ }
+
+ /* Skip leading whitespace and escape sequences. */
+
+ cp = n->string;
+ while (*cp != '\0') {
+ if ('\\' == *cp) {
+ cp++;
+ mandoc_escape((const char **)&cp, NULL, NULL);
+ } else if (isspace((unsigned char)*cp))
+ cp++;
+ else
+ break;
+ }
+
+ /* Skip trailing whitespace. */
+
+ for (sz = strlen(cp); sz; sz--)
+ if ( ! isspace((unsigned char)cp[sz-1]))
+ break;
+
+ /* Skip empty strings. */
+
+ if (sz == 0)
+ return;
+
+ if (*dest == NULL) {
+ *dest = mandoc_strndup(cp, sz);
+ return;
+ }
+
+ mandoc_asprintf(&cp, "%s %*s", *dest, (int)sz, cp);
+ free(*dest);
+ *dest = cp;
+}
+
+/* --- main functions of the roff parser ---------------------------------- */
+