+static void
+flush_deferred_text (struct output_engine *e)
+{
+ struct output_item *deferred_text = e->deferred_text;
+ if (deferred_text)
+ {
+ e->deferred_text = NULL;
+ output_submit__ (e, deferred_text);
+ }
+}
+
+static bool
+defer_text (struct output_engine *e, struct output_item *item)
+{
+ if (item->type != OUTPUT_ITEM_TEXT)
+ return false;
+
+ if (!e->deferred_text)
+ e->deferred_text = output_item_unshare (item);
+ else if (text_item_append (e->deferred_text, item))
+ output_item_unref (item);
+ else
+ {
+ flush_deferred_text (e);
+ e->deferred_text = output_item_unshare (item);
+ }
+ return true;
+}
+
+/* Submits ITEM to the configured output drivers, and transfers ownership to
+ the output subsystem. */
+void
+output_submit (struct output_item *item)
+{
+ struct output_engine *e = engine_stack_top ();
+
+ if (e == NULL)
+ return;
+
+ if (item == NULL)
+ return;
+
+ if (defer_text (e, item))
+ return;
+ flush_deferred_text (e);
+
+ /* XXX heading_vars */
+
+ output_submit__ (e, item);
+}
+
+/* Returns the name of the command currently being parsed, or NULL if no
+ command is being parsed. */
+const char *
+output_get_command_name (void)
+{
+ struct output_engine *e = engine_stack_top ();
+ if (e == NULL)
+ return NULL;
+
+ for (size_t i = e->n_groups; i-- > 0;)
+ if (e->groups[i]->command_name)
+ return e->groups[i]->command_name;
+
+ return NULL;
+}
+
+char *
+output_get_uppercase_command_name (void)
+{
+ const char *command_name = output_get_command_name ();
+ return command_name ? utf8_to_upper (command_name) : NULL;
+}
+
+size_t
+output_open_group (struct output_item *item)
+{
+ struct output_engine *e = engine_stack_top ();
+ if (e == NULL)
+ return 0;
+
+ if (e->n_groups >= e->allocated_groups)
+ e->groups = x2nrealloc (e->groups, &e->allocated_groups,
+ sizeof *e->groups);
+ e->groups[e->n_groups++] = item;
+ if (e->n_groups > 1)
+ group_item_add_child (e->groups[e->n_groups - 2], item);
+
+ return e->n_groups - 1;
+}
+
+void
+output_close_groups (size_t nesting_level)
+{
+ struct output_engine *e = engine_stack_top ();
+ if (e == NULL)
+ return;
+
+ while (e->n_groups > nesting_level)
+ {
+ flush_deferred_text (e);
+
+ struct output_item *group = e->groups[--e->n_groups];
+ if (e->n_groups == 0)
+ output_submit__ (e, group);
+ }
+}
+