X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Foutput%2Fspv%2Fspv-light-decoder.c;fp=src%2Foutput%2Fspv%2Fspv-light-decoder.c;h=f8b07d0b087ce35e4c8a8d65760832d7bf65b39d;hb=bcaaee5f0bd21f443c8dcb5f67114e63d43673af;hp=0000000000000000000000000000000000000000;hpb=1abd7f599dd0d773add0a98fa3b612bc15aaf422;p=pspp diff --git a/src/output/spv/spv-light-decoder.c b/src/output/spv/spv-light-decoder.c new file mode 100644 index 0000000000..f8b07d0b08 --- /dev/null +++ b/src/output/spv/spv-light-decoder.c @@ -0,0 +1,856 @@ +/* PSPP - a program for statistical analysis. + Copyright (C) 2017, 2018 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include + +#include "output/spv/spv-light-decoder.h" + +#include +#include +#include +#include + +#include "libpspp/i18n.h" +#include "libpspp/message.h" +#include "output/pivot-table.h" +#include "output/spv/light-binary-parser.h" +#include "output/spv/spv.h" + +#include "gl/xalloc.h" +#include "gl/xsize.h" + +static char * +to_utf8 (const char *s, const char *encoding) +{ + return recode_string ("UTF-8", encoding, s, strlen (s)); +} + +static char * +to_utf8_if_nonempty (const char *s, const char *encoding) +{ + return s && s[0] ? to_utf8 (s, encoding) : NULL; +} + +static void +convert_widths (const uint32_t *in, uint32_t n, int **out, size_t *n_out) +{ + if (n) + { + *n_out = n; + *out = xnmalloc (n, sizeof **out); + for (size_t i = 0; i < n; i++) + (*out)[i] = in[i]; + } +} + +static void +convert_breakpoints (const struct spvlb_breakpoints *in, + size_t **out, size_t *n_out) +{ + if (in && in->n_breaks) + { + *n_out = in->n_breaks; + *out = xnmalloc (in->n_breaks, sizeof *out); + for (size_t i = 0; i < in->n_breaks; i++) + (*out)[i] = in->breaks[i]; + } +} + +static void +convert_keeps (const struct spvlb_keeps *in, + struct pivot_keep **out, size_t *n_out) +{ + if (in && in->n_keeps) + { + *n_out = in->n_keeps; + *out = xnmalloc (*n_out, sizeof **out); + for (size_t i = 0; i < *n_out; i++) + { + (*out)[i].ofs = in->keeps[i]->offset; + (*out)[i].n = in->keeps[i]->n; + } + } +} + +static struct cell_color +decode_spvlb_color_string (const char *s, uint8_t def) +{ + int r, g, b; + if (sscanf (s, "#%2x%2x%2x", &r, &g, &b) != 3) + { + if (*s) + { + fprintf (stderr, "bad color %s\n", s); + exit (1); + } + r = g = b = def; + } + return (struct cell_color) CELL_COLOR (r, g, b); +} + +static struct cell_color +decode_spvlb_color_u32 (uint32_t x) +{ + return (struct cell_color) { x >> 24, x >> 16, x >> 8, x }; +} + +static struct font_style * +decode_spvlb_font_style (const struct spvlb_font_style *in, + const char *encoding) +{ + if (!in) + return NULL; + + struct font_style *out = xmalloc (sizeof *out); + *out = (struct font_style) { + .bold = in->bold, + .italic = in->italic, + .underline = in->underline, + .fg[0] = decode_spvlb_color_string (in->fg_color, 0x00), + .bg[0] = decode_spvlb_color_string (in->bg_color, 0xff), + .typeface = to_utf8 (in->typeface, encoding), + .size = in->size / 1.33, + }; + out->fg[1] = out->fg[0]; + out->bg[1] = out->bg[0]; + return out; +} + +static enum table_halign +decode_spvlb_halign (uint32_t in) +{ + switch (in) + { + case 0: + return TABLE_HALIGN_CENTER; + + case 2: + return TABLE_HALIGN_LEFT; + + case 4: + return TABLE_HALIGN_RIGHT; + + case 6: + case 61453: + return TABLE_HALIGN_DECIMAL; + + case 0xffffffad: + case 64173: + return TABLE_HALIGN_MIXED; + + default: + fprintf (stderr, "bad cell style halign %"PRIu32"\n", in); + exit (1); + } +} + +static enum table_valign +decode_spvlb_valign (uint32_t in) +{ + switch (in) + { + case 0: + return TABLE_VALIGN_CENTER; + + case 1: + return TABLE_VALIGN_TOP; + + case 3: + return TABLE_VALIGN_BOTTOM; + + default: + fprintf (stderr, "bad cell style valign %"PRIu32"\n", in); + exit (1); + } +} + +static struct cell_style * +decode_spvlb_cell_style (const struct spvlb_cell_style *in) +{ + if (!in) + return NULL; + + struct cell_style *out = xzalloc (sizeof *out); + + out->halign = decode_spvlb_halign (in->halign); + out->valign = decode_spvlb_valign (in->valign); + out->decimal_offset = in->decimal_offset; + out->margin[TABLE_HORZ][0] = in->left_margin; + out->margin[TABLE_HORZ][1] = in->right_margin; + out->margin[TABLE_VERT][0] = in->top_margin; + out->margin[TABLE_VERT][1] = in->bottom_margin; + + return out; +} + +static struct pivot_value *decode_spvlb_value (const struct pivot_table *, + const struct spvlb_value *, + const char *encoding); + +static void +decode_spvlb_argument (const struct pivot_table *table, + const struct spvlb_argument *in, + struct pivot_argument *out, + const char *encoding) +{ + if (in->value) + { + out->n = 1; + out->values = xmalloc (sizeof *out->values); + out->values[0] = decode_spvlb_value (table, in->value, encoding); + } + else + { + out->n = in->n_values; + out->values = xnmalloc (out->n, sizeof *out->values); + for (size_t i = 0; i < out->n; i++) + out->values[i] = decode_spvlb_value (table, in->values[i], encoding); + } +} + +static enum settings_value_show +decode_spvlb_value_show (uint8_t in) +{ + switch (in) + { + case 0: return SETTINGS_VALUE_SHOW_DEFAULT; + case 1: return SETTINGS_VALUE_SHOW_VALUE; + case 2: return SETTINGS_VALUE_SHOW_LABEL; + case 3: return SETTINGS_VALUE_SHOW_BOTH; + default: + fprintf (stderr, "bad value show %"PRIu8"\n", in); + exit (1); + } +} + +static struct pivot_value * +decode_spvlb_value (const struct pivot_table *table, + const struct spvlb_value *in, + const char *encoding) +{ + struct pivot_value *out = xzalloc (sizeof *out); + const struct spvlb_value_mod *vm; + + switch (in->type) + { + case 1: + vm = in->type_01.value_mod; + out->type = PIVOT_VALUE_NUMERIC; + out->numeric.x = in->type_01.x; + out->numeric.format = spv_decode_fmt_spec (in->type_01.format); + break; + + case 2: + vm = in->type_02.value_mod; + out->type = PIVOT_VALUE_NUMERIC; + out->numeric.x = in->type_02.x; + out->numeric.format = spv_decode_fmt_spec (in->type_02.format); + out->numeric.var_name = to_utf8_if_nonempty (in->type_02.var_name, + encoding); + out->numeric.value_label = to_utf8_if_nonempty (in->type_02.value_label, + encoding); + out->numeric.show = decode_spvlb_value_show (in->type_02.show); + break; + + case 3: + vm = in->type_03.value_mod; + out->type = PIVOT_VALUE_TEXT; + out->text.local = to_utf8 (in->type_03.local, encoding); + out->text.c = to_utf8 (in->type_03.c, encoding); + out->text.id = to_utf8 (in->type_03.id, encoding); + out->text.user_provided = !in->type_03.fixed; + break; + + case 4: + vm = in->type_04.value_mod; + out->type = PIVOT_VALUE_STRING; + out->string.s = to_utf8 (in->type_04.s, encoding); + out->string.var_name = to_utf8 (in->type_04.var_name, encoding); + out->string.value_label = to_utf8_if_nonempty (in->type_04.value_label, + encoding); + out->string.show = decode_spvlb_value_show (in->type_04.show); + break; + + case 5: + vm = in->type_05.value_mod; + out->type = PIVOT_VALUE_VARIABLE; + out->variable.var_name = to_utf8 (in->type_05.var_name, encoding); + out->variable.var_label = to_utf8_if_nonempty (in->type_05.var_label, + encoding); + out->variable.show = decode_spvlb_value_show (in->type_05.show); + break; + + case 6: + vm = in->type_06.value_mod; + out->type = PIVOT_VALUE_TEXT; + out->text.local = to_utf8 (in->type_06.local, encoding); + out->text.c = to_utf8 (in->type_06.c, encoding); + out->text.id = to_utf8 (in->type_06.id, encoding); + out->text.user_provided = false; + break; + + case -1: + vm = in->type_else.value_mod; + out->type = PIVOT_VALUE_TEMPLATE; + out->template.local = to_utf8 (in->type_else.template, encoding); + out->template.id = out->template.local; + out->template.n_args = in->type_else.n_args; + out->template.args = xnmalloc (in->type_else.n_args, + sizeof *out->template.args); + for (size_t i = 0; i < out->template.n_args; i++) + decode_spvlb_argument (table, in->type_else.args[i], + &out->template.args[i], encoding); + break; + + default: + assert (0); + } + + if (vm) + { + if (vm->subscript) + out->subscript = to_utf8 (vm->subscript, encoding); + + if (vm->n_refs) + { + out->footnotes = xnmalloc (vm->n_refs, sizeof *out->footnotes); + for (size_t i = 0; i < vm->n_refs; i++) + { + uint16_t idx = vm->refs[i]; + if (idx < table->n_footnotes) + out->footnotes[out->n_footnotes++] = table->footnotes[idx]; + else + { + fprintf (stderr, "bad footnote index: %"PRIu16" >= %zu\n", + idx, table->n_footnotes); + exit (1); + } + } + } + + if (vm->style_pair) + { + out->font_style = decode_spvlb_font_style ( + vm->style_pair->font_style, encoding); + out->cell_style = decode_spvlb_cell_style ( + vm->style_pair->cell_style); + } + + if (vm->template_string + && vm->template_string->id + && vm->template_string->id[0] + && out->type == PIVOT_VALUE_TEMPLATE) + out->template.id = to_utf8 (vm->template_string->id, encoding); + } + + return out; +} + +static void +decode_spvlb_area (const struct spvlb_area *in, struct area_style *out, + const char *encoding) +{ + out->font_style.bold = (in->style & 1) != 0; + out->font_style.italic = (in->style & 2) != 0; + out->font_style.underline = in->underline; + out->font_style.fg[0] = decode_spvlb_color_string (in->fg_color, 0x00); + out->font_style.bg[0] = decode_spvlb_color_string (in->bg_color, 0xff); + out->font_style.typeface = to_utf8 (in->typeface, encoding); + out->font_style.size = in->size / 1.33; + out->font_style.fg[1] = (in->alternate + ? decode_spvlb_color_string (in->alt_fg_color, 0x00) + : out->font_style.fg[0]); + out->font_style.bg[1] = (in->alternate + ? decode_spvlb_color_string (in->alt_bg_color, 0xff) + : out->font_style.bg[0]); + assert (in->halign != 61453); + out->cell_style.halign = decode_spvlb_halign (in->halign); + out->cell_style.valign = decode_spvlb_valign (in->valign); + + /* TABLE_HALIGN_DECIMAL doesn't seem to be a real halign for areas, which is + good because there's no way to indicate the decimal offset. Just in + case: */ + if (out->cell_style.halign == TABLE_HALIGN_DECIMAL) + out->cell_style.halign = TABLE_HALIGN_MIXED; + + out->cell_style.margin[TABLE_HORZ][0] = in->left_margin; + out->cell_style.margin[TABLE_HORZ][1] = in->right_margin; + out->cell_style.margin[TABLE_VERT][0] = in->top_margin; + out->cell_style.margin[TABLE_VERT][1] = in->bottom_margin; +} + +static void decode_spvlb_group (const struct pivot_table *, + struct spvlb_category **, + size_t n_categories, + bool show_label, + struct pivot_category *parent, + struct pivot_dimension *, + const char *encoding); + +static void +decode_spvlb_categories (const struct pivot_table *table, + struct spvlb_category **categories, + size_t n_categories, + struct pivot_category *parent, + struct pivot_dimension *dimension, + const char *encoding) +{ + for (size_t i = 0; i < n_categories; i++) + { + const struct spvlb_category *in = categories[i]; + if (in->group && in->group->merge) + { + decode_spvlb_categories (table, in->group->subcategories, + in->group->n_subcategories, + parent, dimension, encoding); + continue; + } + + struct pivot_category *out = xzalloc (sizeof *out); + out->name = decode_spvlb_value (table, in->name, encoding); + out->parent = parent; + out->dimension = dimension; + if (in->group) + { + decode_spvlb_group (table, in->group->subcategories, + in->group->n_subcategories, + true, out, dimension, encoding); + out->data_index = SIZE_MAX; + out->presentation_index = SIZE_MAX; + } + else + { + out->data_index = in->leaf->leaf_index; + out->presentation_index = dimension->n_leaves; + dimension->n_leaves++; + } + + if (parent->n_subs >= parent->allocated_subs) + parent->subs = x2nrealloc (parent->subs, &parent->allocated_subs, + sizeof *parent->subs); + parent->subs[parent->n_subs++] = out; + } +} + +static void +decode_spvlb_group (const struct pivot_table *table, + struct spvlb_category **categories, + size_t n_categories, bool show_label, + struct pivot_category *category, + struct pivot_dimension *dimension, + const char *encoding) +{ + category->subs = xcalloc (n_categories, sizeof *category->subs); + category->n_subs = 0; + category->allocated_subs = 0; + category->show_label = show_label; + + decode_spvlb_categories (table, categories, n_categories, category, + dimension, encoding); +} + +static void +fill_leaves (struct pivot_category *category, + struct pivot_dimension *dimension) +{ + if (pivot_category_is_group (category)) + { + for (size_t i = 0; i < category->n_subs; i++) + fill_leaves (category->subs[i], dimension); + } + else + { + if (category->data_index >= dimension->n_leaves) + { + fprintf (stderr, "leaf_index %zu >= n_leaves %zu\n", + category->data_index, dimension->n_leaves); + exit (1); + } + if (dimension->data_leaves[category->data_index]) + { + fprintf (stderr, "two leaves with data_index %zu\n", + category->data_index); + exit (1); + } + dimension->data_leaves[category->data_index] = category; + dimension->presentation_leaves[category->presentation_index] = category; + } +} + +static struct pivot_dimension * +decode_spvlb_dimension (const struct pivot_table *table, + const struct spvlb_dimension *in, + size_t idx, const char *encoding) +{ + /* Convert most of the dimension. */ + struct pivot_dimension *out = xzalloc (sizeof *out); + out->level = UINT_MAX; + out->top_index = idx; + out->hide_all_labels = in->props->hide_all_labels; + + out->root = xzalloc (sizeof *out->root); + *out->root = (struct pivot_category) { + .name = decode_spvlb_value (table, in->name, encoding), + .dimension = out, + .data_index = SIZE_MAX, + .presentation_index = SIZE_MAX, + }; + decode_spvlb_group (table, in->categories, in->n_categories, + !in->props->hide_dim_label, out->root, out, encoding); + + /* Allocate and fill the array of leaves now that we know how many there + are. */ + out->data_leaves = xcalloc (out->n_leaves, sizeof *out->data_leaves); + out->presentation_leaves = xcalloc (out->n_leaves, + sizeof *out->presentation_leaves); + out->allocated_leaves = out->n_leaves; + fill_leaves (out->root, out); + for (size_t i = 0; i < out->n_leaves; i++) + { + assert (out->data_leaves[i] != NULL); + assert (out->presentation_leaves[i] != NULL); + } + + return out; +} + +static enum table_stroke +decode_spvlb_stroke (uint32_t stroke_type) +{ + switch (stroke_type) + { + case 0: return TABLE_STROKE_NONE; + case 1: return TABLE_STROKE_SOLID; + case 2: return TABLE_STROKE_DASHED; + case 3: return TABLE_STROKE_THICK; + case 4: return TABLE_STROKE_THIN; + case 5: return TABLE_STROKE_DOUBLE; + + default: + fprintf (stderr, "bad stroke %"PRIu32"\n", stroke_type); + exit (1); + } +} + +static void +decode_spvlb_border (const struct spvlb_border *in, struct pivot_table *table) + +{ + if (in->border_type >= PIVOT_N_BORDERS) + { + fprintf (stderr, "bad border type %"PRIu32"\n", in->border_type); + exit (1); + } + + struct table_border_style *out = &table->borders[in->border_type]; + out->stroke = decode_spvlb_stroke (in->stroke_type); + out->color = decode_spvlb_color_u32 (in->color); +} + +static void +decode_spvlb_axis (const uint32_t *dimension_indexes, size_t n_dimensions, + enum pivot_axis_type axis_type, struct pivot_table *table) +{ + struct pivot_axis *axis = &table->axes[axis_type]; + axis->dimensions = xnmalloc (n_dimensions, sizeof *axis->dimensions); + axis->n_dimensions = n_dimensions; + axis->extent = 1; + for (size_t i = 0; i < n_dimensions; i++) + { + uint32_t idx = dimension_indexes[i]; + if (idx >= table->n_dimensions) + { + fprintf (stderr, "bad dimension index %"PRIu32" >= %zu", + idx, table->n_dimensions); + exit (1); + } + + struct pivot_dimension *d = table->dimensions[idx]; + if (d->level != UINT_MAX) + { + fprintf (stderr, "duplicate dimension %"PRIu32, idx); + exit (1); + } + + axis->dimensions[i] = d; + d->axis_type = axis_type; + d->level = i; + + axis->extent *= d->n_leaves; + } +} + +static void +decode_data_index (uint64_t in, const struct pivot_table *table, + size_t *out) +{ + uint64_t remainder = in; + for (size_t i = table->n_dimensions - 1; i > 0; i--) + { + const struct pivot_dimension *d = table->dimensions[i]; + if (d->n_leaves) + { + out[i] = remainder % d->n_leaves; + remainder /= d->n_leaves; + } + else + out[i] = 0; + } + if (remainder >= table->dimensions[0]->n_leaves) + { + fprintf (stderr, "out of range cell data index %"PRIu64, in); + exit (1); + } + out[0] = remainder; +} + +static void +decode_spvlb_cells (struct spvlb_cell **in, size_t n_in, + struct pivot_table *table, const char *encoding) +{ + if (!table->n_dimensions) + return; + + size_t *dindexes = xnmalloc (table->n_dimensions, sizeof *dindexes); + for (size_t i = 0; i < n_in; i++) + { + decode_data_index (in[i]->index, table, dindexes); + struct pivot_value *value = decode_spvlb_value (table, in[i]->value, + encoding); + pivot_table_put (table, dindexes, table->n_dimensions, value); + } + free (dindexes); +} + +static void +decode_spvlb_footnote (const struct spvlb_footnote *in, const char *encoding, + size_t idx, struct pivot_table *table) +{ + struct pivot_value *content = decode_spvlb_value (table, in->text, encoding); + struct pivot_value *marker = NULL; + if (in->marker) + { + marker = decode_spvlb_value (table, in->marker, encoding); + if (marker->type == PIVOT_VALUE_TEXT) + marker->text.user_provided = false; + } + pivot_table_create_footnote__ (table, idx, marker, content); +} + +static void +decode_current_layer (uint64_t current_layer, struct pivot_table *table) +{ + const struct pivot_axis *axis = &table->axes[PIVOT_AXIS_LAYER]; + table->current_layer = xnmalloc (axis->n_dimensions, + sizeof *table->current_layer); + + for (size_t i = 0; i < axis->n_dimensions; i++) + { + const struct pivot_dimension *d = axis->dimensions[i]; + if (d->n_leaves) + { + table->current_layer[i] = current_layer % d->n_leaves; + current_layer /= d->n_leaves; + } + else + table->current_layer[i] = 0; + } + if (current_layer > 0) + { + fprintf (stderr, "out of range layer data index %"PRIu64, current_layer); + exit (1); + } +} + +char * +decode_spvlb_table (const struct spvlb_table *in, struct pivot_table **outp) +{ + if (in->header->version != 1 && in->header->version != 3) + return xasprintf ("unknown version %"PRIu32" (expected 1 or 3)", + in->header->version); + + struct pivot_table *out = xzalloc (sizeof *out); + out->ref_cnt = 1; + hmap_init (&out->cells); + + const struct spvlb_y1 *y1 = (in->formats->x0 ? in->formats->x0->y1 + : in->formats->x3 ? in->formats->x3->y1 + : NULL); + const char *encoding; + if (y1) + encoding = y1->charset; + else + { + const char *dot = strchr (in->formats->locale, '.'); + encoding = dot ? dot + 1 : "windows-1252"; + } + + /* Display settings. */ + out->show_numeric_markers = !in->ts->show_alphabetic_markers; + out->rotate_inner_column_labels = in->header->rotate_inner_column_labels; + out->rotate_outer_row_labels = in->header->rotate_outer_row_labels; + out->row_labels_in_corner = in->ts->show_row_labels_in_corner; + out->show_grid_lines = in->borders->show_grid_lines; + out->footnote_marker_superscripts = in->ts->footnote_marker_superscripts; + out->omit_empty = in->ts->omit_empty; + + const struct spvlb_x1 *x1 = in->formats->x1; + if (x1) + { + out->show_values = decode_spvlb_value_show (x1->show_values); + out->show_variables = decode_spvlb_value_show (x1->show_variables); + } + + /* Column and row display settings. */ + out->sizing[TABLE_VERT].range[0] = in->header->min_row_height; + out->sizing[TABLE_VERT].range[1] = in->header->max_row_height; + out->sizing[TABLE_HORZ].range[0] = in->header->min_col_width; + out->sizing[TABLE_HORZ].range[1] = in->header->max_col_width; + + convert_widths (in->formats->widths, in->formats->n_widths, + &out->sizing[TABLE_HORZ].widths, + &out->sizing[TABLE_HORZ].n_widths); + + const struct spvlb_x2 *x2 = in->formats->x2; + if (x2) + convert_widths (x2->row_heights, x2->n_row_heights, + &out->sizing[TABLE_VERT].widths, + &out->sizing[TABLE_VERT].n_widths); + + convert_breakpoints (in->ts->row_breaks, + &out->sizing[TABLE_VERT].breaks, + &out->sizing[TABLE_VERT].n_breaks); + convert_breakpoints (in->ts->col_breaks, + &out->sizing[TABLE_HORZ].breaks, + &out->sizing[TABLE_HORZ].n_breaks); + + convert_keeps (in->ts->row_keeps, + &out->sizing[TABLE_VERT].keeps, + &out->sizing[TABLE_VERT].n_keeps); + convert_keeps (in->ts->col_keeps, + &out->sizing[TABLE_HORZ].keeps, + &out->sizing[TABLE_HORZ].n_keeps); + + out->notes = to_utf8_if_nonempty (in->ts->notes, encoding); + out->table_look = to_utf8_if_nonempty (in->ts->table_look, encoding); + + /* Print settings. */ + out->print_all_layers = in->ps->all_layers; + out->paginate_layers = in->ps->paginate_layers; + out->shrink_to_fit[TABLE_HORZ] = in->ps->fit_width; + out->shrink_to_fit[TABLE_VERT] = in->ps->fit_length; + out->top_continuation = in->ps->top_continuation; + out->bottom_continuation = in->ps->bottom_continuation; + out->continuation = xstrdup (in->ps->continuation_string); + out->n_orphan_lines = in->ps->n_orphan_lines; + + /* Format settings. */ + out->epoch = in->formats->y0->epoch; + out->decimal = in->formats->y0->decimal; + out->grouping = in->formats->y0->grouping; + const struct spvlb_custom_currency *cc = in->formats->custom_currency; + for (int i = 0; i < 5; i++) + if (cc && i < cc->n_ccs) + out->ccs[i] = xstrdup (cc->ccs[i]); + out->small = in->formats->x3 ? in->formats->x3->small : 0; + + /* Command information. */ + if (y1) + { + out->command_local = to_utf8 (y1->command_local, encoding); + out->command_c = to_utf8 (y1->command, encoding); + out->language = xstrdup (y1->language); + /* charset? */ + out->locale = xstrdup (y1->locale); + } + + /* Source information. */ + const struct spvlb_x3 *x3 = in->formats->x3; + if (x3) + { + if (x3->dataset && x3->dataset[0] && x3->dataset[0] != 4) + out->dataset = to_utf8 (x3->dataset, encoding); + out->datafile = to_utf8_if_nonempty (x3->datafile, encoding); + out->date = x3->date; + } + + /* Footnotes. + + Any pivot_value might refer to footnotes, so it's important to process the + footnotes early to ensure that those references can be resolved. There is + a possible problem that a footnote might itself reference an + as-yet-unprocessed footnote, but that's OK because footnote references + don't actually look at the footnote contents but only resolve a pointer to + where the footnote will go later. + + Before we really start, create all the footnotes we'll fill in. This is + because sometimes footnotes refer to themselves or to each other and we + don't want to reject those references. */ + const struct spvlb_footnotes *fn = in->footnotes; + if (fn->n_footnotes > 0) + { + pivot_table_create_footnote__ (out, fn->n_footnotes - 1, NULL, NULL); + for (size_t i = 0; i < fn->n_footnotes; i++) + decode_spvlb_footnote (in->footnotes->footnotes[i], encoding, i, out); + } + + /* Title and caption. */ + out->title = decode_spvlb_value (out, in->titles->user_title, encoding); + out->subtype = decode_spvlb_value (out, in->titles->subtype, encoding); + if (in->titles->corner_text) + out->corner_text = decode_spvlb_value (out, in->titles->corner_text, + encoding); + if (in->titles->caption) + out->caption = decode_spvlb_value (out, in->titles->caption, encoding); + + /* Styles. */ + for (size_t i = 0; i < PIVOT_N_AREAS; i++) + decode_spvlb_area (in->areas->areas[i], &out->areas[i], encoding); + for (size_t i = 0; i < PIVOT_N_BORDERS; i++) + decode_spvlb_border (in->borders->borders[i], out); + + /* Dimensions. */ + out->n_dimensions = in->dimensions->n_dims; + out->dimensions = xcalloc (out->n_dimensions, sizeof *out->dimensions); + for (size_t i = 0; i < out->n_dimensions; i++) + out->dimensions[i] = decode_spvlb_dimension (out, in->dimensions->dims[i], + i, encoding); + + /* Axes. */ + size_t a = in->axes->n_layers; + size_t b = in->axes->n_rows; + size_t c = in->axes->n_columns; + if (size_overflow_p (xsum3 (a, b, c)) || a + b + c != out->n_dimensions) + { + fprintf (stderr, "wrong number of dimensions\n"); + exit (1); + } + decode_spvlb_axis (in->axes->layers, in->axes->n_layers, + PIVOT_AXIS_LAYER, out); + decode_spvlb_axis (in->axes->rows, in->axes->n_rows, PIVOT_AXIS_ROW, out); + decode_spvlb_axis (in->axes->columns, in->axes->n_columns, + PIVOT_AXIS_COLUMN, out); + + pivot_table_assign_label_depth (out); + + decode_current_layer (in->ts->current_layer, out); + + /* Data. */ + decode_spvlb_cells (in->cells->cells, in->cells->n_cells, out, encoding); + + *outp = out; + return NULL; +}