+
+void
+output_item_set_command_name (struct output_item *item, const char *name)
+{
+ output_item_set_command_name_nocopy (item, xstrdup_if_nonnull (name));
+}
+
+void
+output_item_set_command_name_nocopy (struct output_item *item, char *name)
+{
+ free (item->command_name);
+ item->command_name = name;
+}
+
+char *
+output_item_get_subtype (const struct output_item *item)
+{
+ return (item->type == OUTPUT_ITEM_TABLE
+ ? pivot_value_to_string (item->table->subtype, item->table)
+ : NULL);
+}
+
+void
+output_item_add_spv_info (struct output_item *item)
+{
+ assert (!output_item_is_shared (item));
+ if (!item->spv_info)
+ item->spv_info = xzalloc (sizeof *item->spv_info);
+}
+\f
+static void
+indent (int indentation)
+{
+ for (int i = 0; i < indentation * 2; i++)
+ putchar (' ');
+}
+
+void
+output_item_dump (const struct output_item *item, int indentation)
+{
+ indent (indentation);
+ if (item->label)
+ printf ("label=\"%s\" ", item->label);
+ if (item->command_name)
+ printf ("command=\"%s\" ", item->command_name);
+ if (!item->show)
+ printf ("(%s) ", item->type == OUTPUT_ITEM_GROUP ? "collapsed" : "hidden");
+
+ switch (item->type)
+ {
+ case OUTPUT_ITEM_CHART:
+ printf ("chart \"%s\"\n", item->chart->title ? item->chart->title : "");
+ break;
+
+ case OUTPUT_ITEM_GROUP:
+ printf ("group\n");
+ for (size_t i = 0; i < item->group.n_children; i++)
+ output_item_dump (item->group.children[i], indentation + 1);
+ break;
+
+ case OUTPUT_ITEM_IMAGE:
+ printf ("image\n");
+ break;
+
+ case OUTPUT_ITEM_MESSAGE:
+ printf ("message\n");
+ break;
+
+ case OUTPUT_ITEM_PAGE_BREAK:
+ printf ("page break\n");
+ break;
+
+ case OUTPUT_ITEM_TABLE:
+ pivot_table_dump (item->table, indentation + 1);
+ break;
+
+ case OUTPUT_ITEM_TEXT:
+ printf ("text %s \"%s\"\n",
+ text_item_subtype_to_string (item->text.subtype),
+ pivot_value_to_string_defaults (item->text.content));
+ break;
+ }
+}
+\f
+void
+output_iterator_init (struct output_iterator *iter,
+ const struct output_item *item)
+{
+ *iter = (struct output_iterator) OUTPUT_ITERATOR_INIT (item);
+}
+
+void
+output_iterator_destroy (struct output_iterator *iter)
+{
+ if (iter)
+ {
+ free (iter->nodes);
+ iter->nodes = NULL;
+ iter->n = iter->allocated = 0;
+ }
+}
+
+void
+output_iterator_next (struct output_iterator *iter)
+{
+ const struct output_item *cur = iter->cur;
+ if (cur)
+ {
+ if (cur->type == OUTPUT_ITEM_GROUP && cur->group.n_children > 0)
+ {
+ if (iter->n >= iter->allocated)
+ iter->nodes = x2nrealloc (iter->nodes, &iter->allocated,
+ sizeof *iter->nodes);
+ iter->nodes[iter->n++] = (struct output_iterator_node) {
+ .group = cur,
+ .idx = 0,
+ };
+ iter->cur = cur->group.children[0];
+ return;
+ }
+
+ for (; iter->n > 0; iter->n--)
+ {
+ struct output_iterator_node *node = &iter->nodes[iter->n - 1];
+ if (++node->idx < node->group->group.n_children)
+ {
+ iter->cur = node->group->group.children[node->idx];
+ return;
+ }
+ }
+
+ iter->cur = NULL;
+ output_iterator_destroy (iter);
+ }
+}