#include <limits.h>
#include <stdlib.h>
#include <string.h>
+#include <unistr.h>
#include "libpspp/i18n.h"
#include "libpspp/message.h"
+#include "libpspp/string-array.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"
+/* Returns a copy of S converted to UTF-8. S might be in UTF-8 already or it
+ might be in ENCODING (yes, this makes no sense). */
static char *
to_utf8 (const char *s, const char *encoding)
{
- return recode_string ("UTF-8", encoding, s, strlen (s));
+ size_t length = strlen (s);
+ return (u8_check (CHAR_CAST (const uint8_t *, s), length)
+ ? recode_string ("UTF-8", encoding, s, length)
+ : xstrdup (s));
}
static char *
decode_spvlb_color_string (const char *s, uint8_t def,
struct cell_color *colorp)
{
- int r, g, b;
+ unsigned int r, g, b;
if (!*s)
r = g = b = def;
else if (sscanf (s, "#%2x%2x%2x", &r, &g, &b) != 3)
out->type = PIVOT_VALUE_NUMERIC;
out->numeric.x = in->type_01.x;
error = spv_decode_fmt_spec (in->type_01.format, &out->numeric.format);
+ out->numeric.honor_small = (in->type_01.format >> 16) == 40;
if (error)
return error;
break;
if (error)
return NULL;
out->string.s = to_utf8 (in->type_04.s, encoding);
+ out->string.hex = (in->type_04.format >> 16) == fmt_to_io (FMT_AHEX);
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);
{
if (vm->n_subscripts)
{
- out->n_subscripts = vm->n_subscripts;
- out->subscripts = xnmalloc (vm->n_subscripts,
- sizeof *out->subscripts);
+ struct pivot_value_ex *ex = pivot_value_ex_rw (out);
+ ex->n_subscripts = vm->n_subscripts;
+ ex->subscripts = xnmalloc (vm->n_subscripts, sizeof *ex->subscripts);
for (size_t i = 0; i < vm->n_subscripts; i++)
- out->subscripts[i] = to_utf8 (vm->subscripts[i], encoding);
+ ex->subscripts[i] = to_utf8 (vm->subscripts[i], encoding);
}
if (vm->n_refs)
{
- out->footnotes = xnmalloc (vm->n_refs, sizeof *out->footnotes);
+ struct pivot_value_ex *ex = pivot_value_ex_rw (out);
+ ex->footnote_indexes = xnmalloc (vm->n_refs,
+ sizeof *ex->footnote_indexes);
+
for (size_t i = 0; i < vm->n_refs; i++)
{
uint16_t idx = vm->refs[i];
idx, table->n_footnotes);
}
- out->footnotes[out->n_footnotes++] = table->footnotes[idx];
+ ex->footnote_indexes[ex->n_footnotes++] = idx;
}
+ pivot_value_sort_footnotes (out);
}
if (vm->style_pair)
{
+ struct pivot_value_ex *ex = pivot_value_ex_rw (out);
error = decode_spvlb_font_style (vm->style_pair->font_style,
- encoding, &out->font_style);
+ encoding, &ex->font_style);
if (!error)
error = decode_spvlb_cell_style (vm->style_pair->cell_style,
- &out->cell_style);
+ &ex->cell_style);
if (error)
{
pivot_value_destroy (out);
if (error)
return error;
+ table_area_style_uninit (out);
*out = (struct table_area_style) {
.font_style = {
.bold = (in->style & 1) != 0,
if (in->border_type >= PIVOT_N_BORDERS)
return xasprintf ("bad border type %"PRIu32, in->border_type);
- struct table_border_style *out = &table->look.borders[in->border_type];
+ struct table_border_style *out = &table->look->borders[in->border_type];
out->color = decode_spvlb_color_u32 (in->color);
return decode_spvlb_stroke (in->stroke_type, &out->stroke);
}
size_t *out)
{
uint64_t remainder = in;
- for (size_t i = table->n_dimensions - 1; i > 0; i--)
+ for (size_t i = table->n_dimensions - 1; i < table->n_dimensions; i--)
{
const struct pivot_dimension *d = table->dimensions[i];
if (d->n_leaves)
else
out[i] = 0;
}
- if (remainder >= table->dimensions[0]->n_leaves)
+ if (remainder)
return xasprintf ("out of range cell data index %"PRIu64, in);
- out[0] = remainder;
return NULL;
}
table->current_layer = xnmalloc (axis->n_dimensions,
sizeof *table->current_layer);
+ uint64_t remainder = 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;
+ table->current_layer[i] = remainder % d->n_leaves;
+ remainder /= d->n_leaves;
}
else
table->current_layer[i] = 0;
}
- if (current_layer > 0)
+ if (remainder > 0)
return xasprintf ("out of range layer data index %"PRIu64, current_layer);
+
return NULL;
}
struct pivot_table *out = xzalloc (sizeof *out);
out->ref_cnt = 1;
hmap_init (&out->cells);
+ out->look = pivot_table_look_new_builtin_default ();
+ out->settings = (struct fmt_settings) FMT_SETTINGS_INIT;
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";
- }
+ const char *encoding = spvlb_table_get_encoding (in);
/* Display settings. */
- out->look.show_numeric_markers = !in->ts->show_alphabetic_markers;
+ out->look->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->look.row_labels_in_corner = in->ts->show_row_labels_in_corner;
+ out->look->row_labels_in_corner = in->ts->show_row_labels_in_corner;
out->show_grid_lines = in->borders->show_grid_lines;
+ out->show_title = true;
out->show_caption = true;
- out->look.footnote_marker_superscripts = in->ts->footnote_marker_superscripts;
- out->look.omit_empty = in->ts->omit_empty;
+ out->look->footnote_marker_superscripts = in->ts->footnote_marker_superscripts;
+ out->look->omit_empty = in->ts->omit_empty;
const struct spvlb_x1 *x1 = in->formats->x1;
if (x1)
goto error;
out->show_caption = x1->show_caption;
+ out->show_title = x1->show_title != 10;
}
/* Column and row display settings. */
- out->look.width_ranges[TABLE_VERT][0] = in->header->min_row_height;
- out->look.width_ranges[TABLE_VERT][1] = in->header->max_row_height;
- out->look.width_ranges[TABLE_HORZ][0] = in->header->min_col_width;
- out->look.width_ranges[TABLE_HORZ][1] = in->header->max_col_width;
+ out->look->width_ranges[TABLE_VERT][0] = in->header->min_row_height;
+ out->look->width_ranges[TABLE_VERT][1] = in->header->max_row_height;
+ out->look->width_ranges[TABLE_HORZ][0] = in->header->min_col_width;
+ out->look->width_ranges[TABLE_HORZ][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_keeps);
out->notes = to_utf8_if_nonempty (in->ts->notes, encoding);
- out->look.name = to_utf8_if_nonempty (in->ts->table_look, encoding);
+ out->look->name = to_utf8_if_nonempty (in->ts->table_look, encoding);
/* Print settings. */
- out->look.print_all_layers = in->ps->all_layers;
- out->look.paginate_layers = in->ps->paginate_layers;
- out->look.shrink_to_fit[TABLE_HORZ] = in->ps->fit_width;
- out->look.shrink_to_fit[TABLE_VERT] = in->ps->fit_length;
- out->look.top_continuation = in->ps->top_continuation;
- out->look.bottom_continuation = in->ps->bottom_continuation;
- out->look.continuation = xstrdup (in->ps->continuation_string);
- out->look.n_orphan_lines = in->ps->n_orphan_lines;
+ out->look->print_all_layers = in->ps->all_layers;
+ out->look->paginate_layers = in->ps->paginate_layers;
+ out->look->shrink_to_fit[TABLE_HORZ] = in->ps->fit_width;
+ out->look->shrink_to_fit[TABLE_VERT] = in->ps->fit_length;
+ out->look->top_continuation = in->ps->top_continuation;
+ out->look->bottom_continuation = in->ps->bottom_continuation;
+ out->look->continuation = to_utf8 (in->ps->continuation_string, encoding);
+ out->look->n_orphan_lines = in->ps->n_orphan_lines;
/* Format settings. */
- out->epoch = in->formats->y0->epoch;
- out->decimal = in->formats->y0->decimal;
+ int epoch = in->formats->y0->epoch;
+ if (epoch >= 1000 && epoch <= 9999)
+ out->settings.epoch = epoch;
+ char decimal = in->formats->y0->decimal;
+ if (decimal == '.' || decimal == ',')
+ out->settings.decimal = decimal;
+ else
+ {
+ /* XXX warn about bad decimal point */
+ }
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]);
+ {
+ if (cc && i < cc->n_ccs)
+ {
+ out->settings.ccs[i] = fmt_number_style_from_string (cc->ccs[i]);
+ /* XXX warn if parsing fails */
+ }
+ }
out->small = in->formats->x3 ? in->formats->x3->small : 0;
/* Command information. */
{
out->command_local = to_utf8 (y1->command_local, encoding);
out->command_c = to_utf8 (y1->command, encoding);
- out->language = xstrdup (y1->language);
+ out->language = to_utf8 (y1->language, encoding);
/* charset? */
- out->locale = xstrdup (y1->locale);
+ out->locale = to_utf8 (y1->locale, encoding);
}
/* Source information. */
/* Styles. */
for (size_t i = 0; i < PIVOT_N_AREAS; i++)
{
- error = decode_spvlb_area (in->areas->areas[i], &out->look.areas[i],
+ error = decode_spvlb_area (in->areas->areas[i], &out->look->areas[i],
encoding);
if (error)
goto error;
pivot_table_unref (out);
return error;
}
+\f
+/* collect_spvlb_strings */
+
+static void
+add_if_nonempty (struct string_array *strings, const char *s)
+{
+ if (s && s[0])
+ string_array_append (strings, s);
+}
+
+static void
+collect_value_mod_strings (struct string_array *strings,
+ const struct spvlb_value_mod *vm)
+{
+ if (vm->template_string)
+ add_if_nonempty (strings, vm->template_string->id);
+
+ if (vm->style_pair && vm->style_pair->font_style)
+ add_if_nonempty (strings, vm->style_pair->font_style->typeface);
+}
+
+static void
+collect_value_strings (struct string_array *strings,
+ const struct spvlb_value *value)
+{
+ if (!value)
+ return;
+
+ switch (value->type)
+ {
+ case 1:
+ collect_value_mod_strings (strings, value->type_01.value_mod);
+ break;
+
+ case 2:
+ collect_value_mod_strings (strings, value->type_02.value_mod);
+ add_if_nonempty (strings, value->type_02.var_name);
+ add_if_nonempty (strings, value->type_02.value_label);
+ break;
+
+ case 3:
+ collect_value_mod_strings (strings, value->type_03.value_mod);
+ add_if_nonempty (strings, value->type_03.local);
+ add_if_nonempty (strings, value->type_03.id);
+ add_if_nonempty (strings, value->type_03.c);
+ break;
+
+ case 4:
+ collect_value_mod_strings (strings, value->type_04.value_mod);
+ add_if_nonempty (strings, value->type_04.value_label);
+ add_if_nonempty (strings, value->type_04.var_name);
+ add_if_nonempty (strings, value->type_04.s);
+ break;
+
+ case 5:
+ collect_value_mod_strings (strings, value->type_05.value_mod);
+ add_if_nonempty (strings, value->type_05.var_name);
+ add_if_nonempty (strings, value->type_05.var_label);
+ break;
+
+ case 6:
+ collect_value_mod_strings (strings, value->type_06.value_mod);
+ add_if_nonempty (strings, value->type_06.local);
+ add_if_nonempty (strings, value->type_06.id);
+ add_if_nonempty (strings, value->type_06.c);
+ break;
+
+ case -1:
+ collect_value_mod_strings (strings, value->type_else.value_mod);
+ add_if_nonempty (strings, value->type_else.template);
+ for (size_t i = 0; i < value->type_else.n_args; i++)
+ {
+ const struct spvlb_argument *a = value->type_else.args[i];
+ collect_value_strings (strings, a->value);
+ for (size_t j = 0; j < a->n_values; j++)
+ collect_value_strings (strings, a->values[j]);
+ }
+ break;
+ }
+}
+
+static void
+collect_category_strings (struct string_array *strings,
+ const struct spvlb_category *cat)
+{
+ collect_value_strings (strings, cat->name);
+ if (cat->group)
+ for (size_t i = 0; i < cat->group->n_subcategories; i++)
+ collect_category_strings (strings, cat->group->subcategories[i]);
+}
+
+/* Adds all of the characters strings in TABLE to STRINGS. */
+void
+collect_spvlb_strings (const struct spvlb_table *table,
+ struct string_array *strings)
+{
+ add_if_nonempty (strings, table->ts->notes);
+ add_if_nonempty (strings, table->ts->table_look);
+ add_if_nonempty (strings, table->ps->continuation_string);
+
+ const struct spvlb_custom_currency *cc = table->formats->custom_currency;
+ if (cc)
+ for (int i = 0; i < cc->n_ccs; i++)
+ add_if_nonempty (strings, cc->ccs[i]);
+
+ const struct spvlb_y1 *y1 = (table->formats->x0 ? table->formats->x0->y1
+ : table->formats->x3 ? table->formats->x3->y1
+ : NULL);
+ if (y1)
+ {
+ add_if_nonempty (strings, y1->command_local);
+ add_if_nonempty (strings, y1->command);
+ add_if_nonempty (strings, y1->language);
+ add_if_nonempty (strings, y1->charset);
+ add_if_nonempty (strings, y1->locale);
+ }
+
+ const struct spvlb_x3 *x3 = table->formats->x3;
+ if (x3)
+ {
+ if (x3->dataset && x3->dataset[0] && x3->dataset[0] != 4)
+ add_if_nonempty (strings, x3->dataset);
+ add_if_nonempty (strings, x3->datafile);
+ }
+
+ for (size_t i = 0; i < table->footnotes->n_footnotes; i++)
+ {
+ const struct spvlb_footnote *f = table->footnotes->footnotes[i];
+ collect_value_strings (strings, f->text);
+ collect_value_strings (strings, f->marker);
+ }
+
+ collect_value_strings (strings, table->titles->user_title);
+ collect_value_strings (strings, table->titles->subtype);
+ collect_value_strings (strings, table->titles->corner_text);
+ collect_value_strings (strings, table->titles->caption);
+
+ for (size_t i = 0; i < PIVOT_N_AREAS; i++)
+ add_if_nonempty (strings, table->areas->areas[i]->typeface);
+
+ for (size_t i = 0; i < table->dimensions->n_dims; i++)
+ {
+ const struct spvlb_dimension *d = table->dimensions->dims[i];
+ collect_value_strings (strings, d->name);
+ for (size_t j = 0; j < d->n_categories; j++)
+ collect_category_strings (strings, d->categories[j]);
+ }
+
+ for (size_t i = 0; i < table->cells->n_cells; i++)
+ collect_value_strings (strings, table->cells->cells[i]->value);
+}
+\f
+/* Returns the encoding that TABLE declares to be in use for its strings.
+ (Watch out, it's not always correct.) */
+const char *
+spvlb_table_get_encoding (const struct spvlb_table *table)
+{
+ const struct spvlb_y1 *y1 = (table->formats->x0 ? table->formats->x0->y1
+ : table->formats->x3 ? table->formats->x3->y1
+ : NULL);
+ if (y1)
+ return y1->charset;
+ else
+ {
+ const char *dot = strchr (table->formats->locale, '.');
+ return dot ? dot + 1 : "windows-1252";
+ }
+}