+static struct pivot_table_sizing
+clone_sizing (const struct pivot_table_sizing *s)
+{
+ return (struct pivot_table_sizing) {
+ .widths = (s->n_widths
+ ? xmemdup (s->widths, s->n_widths * sizeof *s->widths)
+ : NULL),
+ .n_widths = s->n_widths,
+
+ .breaks = (s->n_breaks
+ ? xmemdup (s->breaks, s->n_breaks * sizeof *s->breaks)
+ : NULL),
+ .n_breaks = s->n_breaks,
+
+ .keeps = (s->n_keeps
+ ? xmemdup (s->keeps, s->n_keeps * sizeof *s->keeps)
+ : NULL),
+ .n_keeps = s->n_keeps,
+ };
+}
+
+static struct pivot_footnote **
+clone_footnotes (struct pivot_footnote **old, size_t n)
+{
+ if (!n)
+ return NULL;
+
+ struct pivot_footnote **new = xmalloc (n * sizeof *new);
+ for (size_t i = 0; i < n; i++)
+ {
+ new[i] = xmalloc (sizeof *new[i]);
+ *new[i] = (struct pivot_footnote) {
+ .idx = old[i]->idx,
+ .content = pivot_value_clone (old[i]->content),
+ .marker = pivot_value_clone (old[i]->marker),
+ .show = old[i]->show,
+ };
+ }
+ return new;
+}
+
+static struct pivot_category *
+clone_category (struct pivot_category *old,
+ struct pivot_dimension *new_dimension,
+ struct pivot_category *new_parent)
+{
+ struct pivot_category *new = xmalloc (sizeof *new);
+ *new = (struct pivot_category) {
+ .name = pivot_value_clone (old->name),
+ .parent = new_parent,
+ .dimension = new_dimension,
+ .label_depth = old->label_depth,
+ .extra_depth = old->extra_depth,
+
+ .subs = (old->n_subs
+ ? xcalloc (old->n_subs, sizeof *new->subs)
+ : NULL),
+ .n_subs = old->n_subs,
+ .allocated_subs = old->n_subs,
+
+ .show_label = old->show_label,
+ .show_label_in_corner = old->show_label_in_corner,
+
+ .format = old->format,
+ .group_index = old->group_index,
+ .data_index = old->data_index,
+ .presentation_index = old->presentation_index,
+ };
+
+ if (pivot_category_is_leaf (old))
+ {
+ assert (new->data_index < new_dimension->n_leaves);
+ new->dimension->data_leaves[new->data_index] = new;
+
+ assert (new->presentation_index < new_dimension->n_leaves);
+ new->dimension->presentation_leaves[new->presentation_index] = new;
+ }
+
+ for (size_t i = 0; i < new->n_subs; i++)
+ new->subs[i] = clone_category (old->subs[i], new_dimension, new);
+
+ return new;
+}
+
+static struct pivot_dimension *
+clone_dimension (struct pivot_dimension *old, struct pivot_table *new_pt)
+{
+ struct pivot_dimension *new = xmalloc (sizeof *new);
+ *new = (struct pivot_dimension) {
+ .table = new_pt,
+ .axis_type = old->axis_type,
+ .level = old->level,
+ .top_index = old->top_index,
+ .data_leaves = xcalloc (old->n_leaves , sizeof *new->data_leaves),
+ .presentation_leaves = xcalloc (old->n_leaves
+ , sizeof *new->presentation_leaves),
+ .n_leaves = old->n_leaves,
+ .allocated_leaves = old->n_leaves,
+ .hide_all_labels = old->hide_all_labels,
+ .label_depth = old->label_depth,
+ };
+
+ new->root = clone_category (old->root, new, NULL);
+
+ return new;
+}
+
+static struct pivot_dimension **
+clone_dimensions (struct pivot_dimension **old, size_t n,
+ struct pivot_table *new_pt)
+{
+ if (!n)
+ return NULL;
+
+ struct pivot_dimension **new = xmalloc (n * sizeof *new);
+ for (size_t i = 0; i < n; i++)
+ new[i] = clone_dimension (old[i], new_pt);
+ return new;
+}
+
+struct pivot_table *
+pivot_table_unshare (struct pivot_table *old)
+{
+ assert (old->ref_cnt > 0);
+ if (old->ref_cnt == 1)
+ return old;
+
+ pivot_table_unref (old);
+
+ struct pivot_table *new = xmalloc (sizeof *new);
+ *new = (struct pivot_table) {
+ .ref_cnt = 1,
+
+ .look = pivot_table_look_ref (old->look),
+
+ .rotate_inner_column_labels = old->rotate_inner_column_labels,
+ .rotate_outer_row_labels = old->rotate_outer_row_labels,
+ .show_grid_lines = old->show_grid_lines,
+ .show_title = old->show_title,
+ .show_caption = old->show_caption,
+ .current_layer = (old->current_layer
+ ? xmemdup (old->current_layer,
+ old->axes[PIVOT_AXIS_LAYER].n_dimensions
+ * sizeof *new->current_layer)
+ : NULL),
+ .show_values = old->show_values,
+ .show_variables = old->show_variables,
+ .weight_format = old->weight_format,
+
+ .sizing = {
+ [TABLE_HORZ] = clone_sizing (&old->sizing[TABLE_HORZ]),
+ [TABLE_VERT] = clone_sizing (&old->sizing[TABLE_VERT]),
+ },
+
+ .settings = fmt_settings_copy (&old->settings),
+ .grouping = old->grouping,
+ .small = old->small,
+
+ .command_local = xstrdup_if_nonnull (old->command_local),
+ .command_c = xstrdup_if_nonnull (old->command_c),
+ .language = xstrdup_if_nonnull (old->language),
+ .locale = xstrdup_if_nonnull (old->locale),
+
+ .dataset = xstrdup_if_nonnull (old->dataset),
+ .datafile = xstrdup_if_nonnull (old->datafile),
+ .date = old->date,
+
+ .footnotes = clone_footnotes (old->footnotes, old->n_footnotes),
+ .n_footnotes = old->n_footnotes,
+ .allocated_footnotes = old->n_footnotes,
+
+ .title = pivot_value_clone (old->title),
+ .subtype = pivot_value_clone (old->subtype),
+ .corner_text = pivot_value_clone (old->corner_text),
+ .caption = pivot_value_clone (old->caption),
+ .notes = xstrdup_if_nonnull (old->notes),
+
+ .dimensions = clone_dimensions (old->dimensions, old->n_dimensions, new),
+ .n_dimensions = old->n_dimensions,
+
+ .cells = HMAP_INITIALIZER (new->cells),
+ };
+
+ for (size_t i = 0; i < PIVOT_N_AXES; i++)
+ {
+ struct pivot_axis *new_axis = &new->axes[i];
+ const struct pivot_axis *old_axis = &old->axes[i];
+
+ *new_axis = (struct pivot_axis) {
+ .dimensions = xmalloc (old_axis->n_dimensions
+ * sizeof *new_axis->dimensions),
+ .n_dimensions = old_axis->n_dimensions,
+ .extent = old_axis->extent,
+ .label_depth = old_axis->label_depth,
+ };
+
+ for (size_t i = 0; i < new_axis->n_dimensions; i++)
+ new_axis->dimensions[i] = new->dimensions[
+ old_axis->dimensions[i]->top_index];
+ }
+
+ const struct pivot_cell *old_cell;
+ size_t *dindexes = xmalloc (old->n_dimensions * sizeof *dindexes);
+ HMAP_FOR_EACH (old_cell, struct pivot_cell, hmap_node, &old->cells)
+ {
+ for (size_t i = 0; i < old->n_dimensions; i++)
+ dindexes[i] = old_cell->idx[i];
+ struct pivot_cell *new_cell
+ = pivot_table_insert_cell (new, dindexes);
+ new_cell->value = pivot_value_clone (old_cell->value);
+ }
+ free (dindexes);
+
+ return new;
+}
+