#include "libpspp/i18n.h"
#include "libpspp/message.h"
#include "libpspp/str.h"
+#include "output/driver.h"
#include "output/output-item.h"
#include "xmalloca.h"
struct dataset *ds, enum cmd_state state)
{
const struct command *command = NULL;
+ size_t nesting_level = SIZE_MAX;
enum cmd_result result;
- bool opened = false;
int n_tokens;
/* Read the command's first token. */
result = CMD_FAILURE;
goto finish;
}
- output_item_submit (group_open_item_create_nocopy (
- utf8_to_title (command->name),
- utf8_to_title (command->name)));
- opened = true;
+
+ nesting_level = output_open_group (group_item_create_nocopy (
+ utf8_to_title (command->name),
+ utf8_to_title (command->name)));
if (command->function == NULL)
{
while (lex_token (lexer) == T_ENDCMD)
lex_get (lexer);
- if (opened)
- output_item_submit (group_close_item_create ());
+ if (nesting_level != SIZE_MAX)
+ output_close_groups (nesting_level);
return result;
}
}
static void
-ascii_submit (struct output_driver *driver,
- const struct output_item *item)
+ascii_submit (struct output_driver *driver, const struct output_item *item)
{
struct ascii_driver *a = ascii_driver_cast (driver);
-
if (a->error)
return;
case OUTPUT_ITEM_CHART:
if (a->chart_file_name != NULL)
{
- char *file_name = xr_draw_png_chart (item->chart, a->chart_file_name,
- ++a->chart_cnt, &a->fg,
- &a->bg);
+ char *file_name = xr_draw_png_chart (
+ item->chart, a->chart_file_name, ++a->chart_cnt, &a->fg, &a->bg);
if (file_name != NULL)
{
struct output_item *text_item = text_item_create_nocopy (
ascii_output_table_item_unref (
a, text_item_to_table_item (
message_item_to_text_item (
- output_item_ref (item))));
+ output_item_ref (item))));
break;
- case OUTPUT_ITEM_GROUP_OPEN:
- case OUTPUT_ITEM_GROUP_CLOSE:
+ case OUTPUT_ITEM_GROUP:
+ NOT_REACHED ();
+
case OUTPUT_ITEM_PAGE_BREAK:
case OUTPUT_ITEM_PAGE_SETUP:
break;
static const struct output_driver_class ascii_driver_class =
{
- "text",
- ascii_destroy,
- ascii_submit,
- ascii_flush,
+ .name = "text",
+ .destroy = ascii_destroy,
+ .submit = ascii_submit,
+ .flush = ascii_flush,
};
\f
static char *ascii_reserve (struct ascii_driver *, int y, int x0, int x1,
src/output/pivot-table.h \
src/output/render.c \
src/output/render.h \
+ src/output/select.c \
+ src/output/select.h \
src/output/spv-driver.c \
src/output/table-provider.h \
src/output/table.c \
item = output_item_ref (item_);
break;
- case OUTPUT_ITEM_GROUP_OPEN:
- case OUTPUT_ITEM_GROUP_CLOSE:
+ case OUTPUT_ITEM_GROUP:
case OUTPUT_ITEM_PAGE_SETUP:
return NULL;
fsm->cairo = NULL;
break;
- case OUTPUT_ITEM_GROUP_OPEN:
- case OUTPUT_ITEM_GROUP_CLOSE:
+ case OUTPUT_ITEM_GROUP:
case OUTPUT_ITEM_MESSAGE:
case OUTPUT_ITEM_PAGE_BREAK:
case OUTPUT_ITEM_PAGE_SETUP:
fsm->cairo = NULL;
break;
- case OUTPUT_ITEM_GROUP_OPEN:
- case OUTPUT_ITEM_GROUP_CLOSE:
+ case OUTPUT_ITEM_GROUP:
case OUTPUT_ITEM_MESSAGE:
case OUTPUT_ITEM_PAGE_BREAK:
case OUTPUT_ITEM_PAGE_SETUP:
used = xr_fsm_draw_table (fsm, space);
break;
- case OUTPUT_ITEM_GROUP_OPEN:
- case OUTPUT_ITEM_GROUP_CLOSE:
+ case OUTPUT_ITEM_GROUP:
case OUTPUT_ITEM_MESSAGE:
case OUTPUT_ITEM_PAGE_SETUP:
case OUTPUT_ITEM_TEXT:
/* Current output item. */
struct xr_fsm *fsm;
- struct output_item *item;
+ struct output_iterator iter;
+ struct output_item *root_item;
int slice_idx;
+ const char *label;
- /* Grouping, for constructing the outline for PDFs.
-
- The 'group_ids' were returned by cairo_pdf_surface_add_outline() and
- represent the groups within which upcoming output is nested. The
- 'group_opens' will be passed to cairo_pdf_surface_add_outline() when the
- next item is rendered (we defer it so that the location associated with
- the outline item can be the first object actually output in it). */
- int *group_ids;
- size_t n_group_ids, allocated_group_ids;
- struct output_item **group_opens;
- size_t n_opens, allocated_opens;
+ /* Grouping, for constructing the outline for PDFs. */
+ struct outline_node
+ {
+ const struct output_item *item;
+ int group_id;
+ }
+ *nodes;
+ size_t n_nodes, allocated_nodes;
/* Current output page. */
cairo_t *cr;
{
if (p)
{
- free (p->group_ids);
- for (size_t i = 0; i < p->n_opens; i++)
- output_item_unref (p->group_opens[i]);
- free (p->group_opens);
+ free (p->nodes);
xr_page_style_unref (p->page_style);
xr_fsm_style_unref (p->fsm_style);
xr_fsm_destroy (p->fsm);
- output_item_unref (p->item);
+ output_iterator_destroy (&p->iter);
+ output_item_unref (p->root_item);
if (p->cr)
{
bool
xr_pager_has_item (const struct xr_pager *p)
{
- return p->item != NULL;
+ return p->root_item != NULL;
}
void
xr_pager_add_item (struct xr_pager *p, const struct output_item *item)
{
- assert (!p->item);
- p->item = output_item_ref (item);
- p->slice_idx = 0;
+ assert (!p->root_item);
+ p->root_item = output_item_ref (item);
+ output_iterator_init (&p->iter, item);
xr_pager_run (p);
}
bool
xr_pager_needs_new_page (struct xr_pager *p)
{
- if (p->item && (!p->cr || p->y >= p->fsm_style->size[V]))
+ if (p->root_item && (!p->cr || p->y >= p->fsm_style->size[V]))
{
xr_pager_finish_page (p);
return true;
static void
xr_pager_run (struct xr_pager *p)
{
- if (p->item && p->cr && p->y < p->fsm_style->size[V])
+ if (!p->root_item || !p->cr || p->y >= p->fsm_style->size[V])
+ return;
+
+ for (;;)
{
- if (!p->fsm)
+ /* Make sure we've got an object to render. */
+ while (!p->fsm)
{
- if (p->item->type == OUTPUT_ITEM_GROUP_OPEN)
- {
- if (p->n_opens >= p->allocated_opens)
- p->group_opens = x2nrealloc (p->group_opens,
- &p->allocated_opens,
- sizeof p->group_opens);
- p->group_opens[p->n_opens++] = output_item_ref (p->item);
- }
- else if (p->item->type == OUTPUT_ITEM_GROUP_CLOSE)
+ /* If there are no remaining objects to render, then we're done. */
+ if (!p->iter.cur)
{
- if (p->n_opens)
- output_item_unref (p->group_opens[--p->n_opens]);
- else if (p->n_group_ids)
- p->n_group_ids--;
- else
- {
- /* Something wrong! */
- }
+ output_item_unref (p->root_item);
+ p->root_item = NULL;
+ return;
}
- p->fsm = xr_fsm_create_for_printing (p->item, p->fsm_style, p->cr);
- if (!p->fsm)
+ /* Prepare to render the current object. */
+ p->fsm = xr_fsm_create_for_printing (p->iter.cur, p->fsm_style,
+ p->cr);
+ p->label = output_item_get_label (p->iter.cur);
+ p->slice_idx = 0;
+ while (p->n_nodes > p->iter.n)
+ p->n_nodes--;
+ while (p->n_nodes)
{
- output_item_unref (p->item);
- p->item = NULL;
+ size_t i = p->n_nodes - 1;
+ if (p->nodes[i].item == p->iter.nodes[i].group)
+ break;
- return;
+ p->nodes--;
}
+ while (p->n_nodes < p->iter.n)
+ {
+ if (p->n_nodes >= p->allocated_nodes)
+ p->nodes = x2nrealloc (p->nodes, &p->allocated_nodes,
+ sizeof *p->nodes);
+ size_t i = p->n_nodes++;
+ p->nodes[i] = (struct outline_node) {
+ .item = p->iter.nodes[i].group,
+ };
+ }
+ output_iterator_next (&p->iter);
}
- for (;;)
+ char *dest_name = NULL;
+ if (p->page_style->include_outline)
{
- char *dest_name = NULL;
- if (p->page_style->include_outline)
- {
- static int counter = 0;
- dest_name = xasprintf ("dest%d", counter++);
- char *attrs = xasprintf ("name='%s'", dest_name);
- cairo_tag_begin (p->cr, CAIRO_TAG_DEST, attrs);
- free (attrs);
- }
+ static int counter = 0;
+ dest_name = xasprintf ("dest%d", counter++);
+ char *attrs = xasprintf ("name='%s'", dest_name);
+ cairo_tag_begin (p->cr, CAIRO_TAG_DEST, attrs);
+ free (attrs);
+ }
+
+ int spacing = p->fsm_style->object_spacing;
+ int chunk = xr_fsm_draw_slice (p->fsm, p->cr,
+ p->fsm_style->size[V] - p->y);
+ p->y += chunk + spacing;
+ cairo_translate (p->cr, 0, xr_to_pt (chunk + spacing));
- int spacing = p->fsm_style->object_spacing;
- int chunk = xr_fsm_draw_slice (p->fsm, p->cr,
- p->fsm_style->size[V] - p->y);
- p->y += chunk + spacing;
- cairo_translate (p->cr, 0, xr_to_pt (chunk + spacing));
+ if (p->page_style->include_outline)
+ {
+ cairo_tag_end (p->cr, CAIRO_TAG_DEST);
- if (p->page_style->include_outline)
+ if (chunk && p->slice_idx++ == 0)
{
- cairo_tag_end (p->cr, CAIRO_TAG_DEST);
+ char *attrs = xasprintf ("dest='%s'", dest_name);
- if (chunk && p->slice_idx++ == 0)
+ int parent_group_id = CAIRO_PDF_OUTLINE_ROOT;
+ for (size_t i = 0; i < p->n_nodes; i++)
{
- char *attrs = xasprintf ("dest='%s'", dest_name);
-
- int parent_group_id = (p->n_group_ids
- ? p->group_ids[p->n_group_ids - 1]
- : CAIRO_PDF_OUTLINE_ROOT);
- for (size_t i = 0; i < p->n_opens; i++)
+ struct outline_node *node = &p->nodes[i];
+ if (!node->group_id)
{
- parent_group_id = add_outline (
- p->cr, parent_group_id,
- output_item_get_label (p->group_opens[i]),
- attrs, CAIRO_PDF_OUTLINE_FLAG_OPEN);
- output_item_unref (p->group_opens[i]);
-
- if (p->n_group_ids >= p->allocated_group_ids)
- p->group_ids = x2nrealloc (p->group_ids,
- &p->allocated_group_ids,
- sizeof *p->group_ids);
- p->group_ids[p->n_group_ids++] = parent_group_id;
+ const char *label = output_item_get_label (node->item);
+ node->group_id = add_outline (
+ p->cr, parent_group_id, label, attrs,
+ CAIRO_PDF_OUTLINE_FLAG_OPEN);
}
- p->n_opens = 0;
-
- add_outline (p->cr, parent_group_id,
- output_item_get_label (p->item), attrs, 0);
- free (attrs);
+ parent_group_id = node->group_id;
}
- free (dest_name);
- }
- if (xr_fsm_is_empty (p->fsm))
- {
- xr_fsm_destroy (p->fsm);
- p->fsm = NULL;
- output_item_unref (p->item);
- p->item = NULL;
- return;
- }
- else if (!chunk)
- {
- assert (p->y > 0);
- p->y = INT_MAX;
- return;
+ add_outline (p->cr, parent_group_id, p->label, attrs, 0);
+ free (attrs);
}
+ free (dest_name);
+ }
+
+ if (xr_fsm_is_empty (p->fsm))
+ {
+ xr_fsm_destroy (p->fsm);
+ p->fsm = NULL;
+ }
+ else if (!chunk)
+ {
+ assert (p->y > 0);
+ p->y = INT_MAX;
+ return;
}
}
}
static const struct output_driver_class cairo_driver_class =
{
- "cairo",
- xr_destroy,
- xr_submit,
- NULL,
+ .name = "cairo",
+ .destroy = xr_destroy,
+ .submit = xr_submit,
+ .handles_groups = true,
};
case OUTPUT_ITEM_CHART:
break;
- case OUTPUT_ITEM_GROUP_OPEN:
- break;
-
- case OUTPUT_ITEM_GROUP_CLOSE:
- break;
+ case OUTPUT_ITEM_GROUP:
+ NOT_REACHED ();
case OUTPUT_ITEM_IMAGE:
break;
static const struct output_driver_class csv_driver_class =
{
- "csv",
- csv_destroy,
- csv_submit,
- csv_flush,
+ .name = "csv",
+ .destroy = csv_destroy,
+ .submit = csv_submit,
+ .flush = csv_flush,
};
#include "output/driver.h"
struct output_item;
+struct output_iterator;
struct string_map;
struct file_handle;
it doesn't make sense for DRIVER to be used this way, then this function
need not do anything. */
void (*flush) (struct output_driver *driver);
- };
+ /* Ordinarily, the core driver code will skip passing hidden output items
+ to 'submit'. If this member is true, the core driver hands them to the
+ driver to let it handle them itself. */
+ bool handles_show;
+
+ /* Ordinarily, the core driver code will flatten groups of output items
+ before passing them to 'submit'. If this member is true, the core
+ driver code leaves them in place for the driver to handle. */
+ bool handles_groups;
+ };
/* An abstract way for the output subsystem to create an output driver. */
struct output_driver_factory
char *command_name; /* Name of command being processed. */
char *title, *subtitle; /* Components of page title. */
- /* Output grouping stack.
-
- TEXT_ITEM_GROUP_OPEN pushes a group on the stack and
- TEXT_ITEM_GROUP_CLOSE pops one off. */
- char **groups; /* Command names of nested sections. */
+ /* Output grouping stack. */
+ struct output_item **groups;
size_t n_groups;
size_t allocated_groups;
free (e->command_name);
free (e->title);
free (e->subtitle);
- for (size_t i = 0; i < e->n_groups; i++)
- free (e->groups[i]);
+ if (e->n_groups)
+ output_item_unref (e->groups[0]);
free (e->groups);
string_map_destroy (&e->heading_vars);
free (e);
string_set_insert (formats, (*fp)->extension);
}
+static bool
+output_driver_should_show (const struct output_driver *d,
+ const struct output_item *item)
+{
+ enum settings_output_type type = SETTINGS_OUTPUT_RESULT;
+ switch (item->type)
+ {
+ case OUTPUT_ITEM_MESSAGE:
+ type = (item->message->severity == MSG_S_NOTE
+ ? SETTINGS_OUTPUT_NOTE
+ : SETTINGS_OUTPUT_ERROR);
+ break;
+
+ case OUTPUT_ITEM_TEXT:
+ if (item->text.subtype == TEXT_ITEM_SYNTAX)
+ type = SETTINGS_OUTPUT_SYNTAX;
+ break;
+
+ case OUTPUT_ITEM_CHART:
+ case OUTPUT_ITEM_GROUP:
+ case OUTPUT_ITEM_IMAGE:
+ case OUTPUT_ITEM_PAGE_BREAK:
+ case OUTPUT_ITEM_PAGE_SETUP:
+ case OUTPUT_ITEM_TABLE:
+ break;
+ }
+
+ return (settings_get_output_routing (type) & d->device_type) != 0;
+}
+
+/* Adds to OUT the subset of IN that driver D should show, considering routing
+ and visibility of each item, and flattening groups for drivers that don't
+ handle them internally. */
+static void
+make_driver_output_subset (const struct output_item *in,
+ const struct output_driver *d,
+ struct output_item *out)
+{
+ if (in->type == OUTPUT_ITEM_GROUP)
+ {
+ /* If we should include the group itself, then clone IN inside OUT, and
+ add any children to the clone instead to OUT directly. */
+ if (output_driver_should_show (d, in) && d->class->handles_groups)
+ {
+ struct output_item *group = group_item_clone_empty (in);
+ group_item_add_child (out, group);
+ out = group;
+ }
+
+ for (size_t i = 0; i < in->group.n_children; i++)
+ make_driver_output_subset (in->group.children[i], d, out);
+ }
+ else
+ {
+ if (output_driver_should_show (d, in)
+ && (in->show || d->class->handles_show))
+ group_item_add_child (out, output_item_ref (in));
+ }
+}
+
static void
output_submit__ (struct output_engine *e, struct output_item *item)
{
+ if (e->n_groups > 0)
+ {
+ group_item_add_child (e->groups[e->n_groups - 1], item);
+ return;
+ }
+
struct llx *llx, *next;
llx_for_each_safe (llx, next, &e->drivers)
{
struct output_driver *d = llx_data (llx);
- enum settings_output_type type = SETTINGS_OUTPUT_RESULT;
- switch (item->type)
- {
- case OUTPUT_ITEM_MESSAGE:
- type = (item->message->severity == MSG_S_NOTE
- ? SETTINGS_OUTPUT_NOTE
- : SETTINGS_OUTPUT_ERROR);
- break;
-
- case OUTPUT_ITEM_TEXT:
- if (item->text.subtype == TEXT_ITEM_SYNTAX)
- type = SETTINGS_OUTPUT_SYNTAX;
- break;
-
- case OUTPUT_ITEM_CHART:
- case OUTPUT_ITEM_GROUP_OPEN:
- case OUTPUT_ITEM_GROUP_CLOSE:
- case OUTPUT_ITEM_IMAGE:
- case OUTPUT_ITEM_PAGE_BREAK:
- case OUTPUT_ITEM_PAGE_SETUP:
- case OUTPUT_ITEM_TABLE:
- break;
- }
-
- if (settings_get_output_routing (type) & d->device_type)
- d->class->submit (d, item);
+ struct output_item *root = root_item_create ();
+ make_driver_output_subset (item, d, root);
+ for (size_t i = 0; i < root->group.n_children; i++)
+ d->class->submit (d, root->group.children[i]);
+ output_item_unref (root);
}
output_item_unref (item);
return;
flush_deferred_text (e);
- switch (item->type)
- {
- case OUTPUT_ITEM_GROUP_OPEN:
- if (e->n_groups >= e->allocated_groups)
- e->groups = x2nrealloc (e->groups, &e->allocated_groups,
- sizeof *e->groups);
- e->groups[e->n_groups] = xstrdup_if_nonnull (item->command_name);
- e->n_groups++;
- break;
-
- case OUTPUT_ITEM_GROUP_CLOSE:
- assert (e->n_groups > 0);
-
- size_t idx = --e->n_groups;
- free (e->groups[idx]);
-
- char *key = xasprintf ("Head%zu", idx);
- free (string_map_find_and_delete (&e->heading_vars, key));
- free (key);
- break;
-
- case OUTPUT_ITEM_TEXT:
- {
- enum text_item_subtype st = item->text.subtype;
- char *key = (st == TEXT_ITEM_TITLE ? xasprintf ("Head%zu", e->n_groups)
- : st == TEXT_ITEM_PAGE_TITLE ? xstrdup ("PageTitle")
- : NULL);
- if (key)
- string_map_replace_nocopy (&e->heading_vars, key,
- text_item_get_plain_text (item));
- }
- break;
-
- case OUTPUT_ITEM_CHART:
- case OUTPUT_ITEM_IMAGE:
- case OUTPUT_ITEM_MESSAGE:
- case OUTPUT_ITEM_PAGE_BREAK:
- case OUTPUT_ITEM_PAGE_SETUP:
- case OUTPUT_ITEM_TABLE:
- break;
- }
+ /* XXX heading_vars */
output_submit__ (e, item);
}
return NULL;
for (size_t i = e->n_groups; i-- > 0;)
- if (e->groups[i])
- return e->groups[i];
+ if (e->groups[i]->command_name)
+ return e->groups[i]->command_name;
return NULL;
}
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);
+ }
+}
+
/* Flushes output to screen devices, so that the user can see
output that doesn't fill up an entire page. */
void
string_map_replace (&e->heading_vars, "Filename", filename);
}
-
-size_t
-output_get_group_level (void)
-{
- struct output_engine *e = engine_stack_top ();
-
- return e->n_groups;
-}
\f
void
output_driver_init (struct output_driver *driver,
const char *output_get_command_name (void);
char *output_get_uppercase_command_name (void);
-size_t output_get_group_level (void);
+size_t output_open_group (struct output_item *);
+void output_close_groups (size_t nesting_level);
void output_driver_parse_option (const char *option,
struct string_map *options);
}
static void
-html_submit (struct output_driver *driver, const struct output_item *item)
+html_submit__ (struct output_driver *driver, const struct output_item *item,
+ int level)
{
struct html_driver *html = html_driver_cast (driver);
}
break;
- case OUTPUT_ITEM_GROUP_OPEN:
- break;
-
- case OUTPUT_ITEM_GROUP_CLOSE:
+ case OUTPUT_ITEM_GROUP:
+ for (size_t i = 0; i < item->group.n_children; i++)
+ html_submit__ (driver, item->group.children[i], level + 1);
break;
case OUTPUT_ITEM_IMAGE:
case TEXT_ITEM_TITLE:
{
- int level = MIN (5, output_get_group_level ()) + 1;
- char tag[3] = { 'H', level + '1', '\0' };
+ char tag[3] = { 'H', MIN (5, level) + '0', '\0' };
print_title_tag (html->file, tag, s);
}
break;
}
}
+static void
+html_submit (struct output_driver *driver, const struct output_item *item)
+{
+ html_submit__ (driver, item, 1);
+}
+
/* Write TEXT to file F, escaping characters as necessary for HTML. Spaces are
replaced by SPACE, which should be " " or " " New-lines are replaced by
NEWLINE, which might be "<BR>" or "\n" or something else appropriate. */
static const struct output_driver_class html_driver_class =
{
- "html",
- html_destroy,
- html_submit,
- NULL,
+ .name = "html",
+ .destroy = html_destroy,
+ .submit = html_submit,
+ .handles_groups = true,
};
journal_output (j, text_item_get_plain_text (item));
break;
+ case OUTPUT_ITEM_GROUP:
+ for (size_t i = 0; i < item->group.n_children; i++)
+ journal_submit (driver, item->group.children[i]);
+ break;
+
case OUTPUT_ITEM_CHART:
- case OUTPUT_ITEM_GROUP_OPEN:
- case OUTPUT_ITEM_GROUP_CLOSE:
case OUTPUT_ITEM_IMAGE:
case OUTPUT_ITEM_PAGE_BREAK:
case OUTPUT_ITEM_PAGE_SETUP:
static const struct output_driver_class journal_class =
{
- "journal",
- journal_destroy,
- journal_submit,
- NULL /* flush */
+ .name = "journal",
+ .destroy = journal_destroy,
+ .submit = journal_submit,
};
-
\f
/* Enables journaling. */
static const struct output_driver_class msglog_class =
{
- "msglog",
- msglog_destroy,
- msglog_submit,
- NULL
+ .name = "msglog",
+ .destroy = msglog_destroy,
+ .submit = msglog_submit,
};
case OUTPUT_ITEM_CHART:
break;
- case OUTPUT_ITEM_GROUP_OPEN:
- break;
-
- case OUTPUT_ITEM_GROUP_CLOSE:
- break;
+ case OUTPUT_ITEM_GROUP:
+ NOT_REACHED ();
case OUTPUT_ITEM_IMAGE:
break;
static const struct output_driver_class odt_driver_class =
{
- "odf",
- odt_destroy,
- odt_submit,
- NULL,
+ .name = "odf",
+ .destroy = odt_destroy,
+ .submit = odt_submit,
};
#include "libpspp/cast.h"
#include "libpspp/message.h"
#include "libpspp/str.h"
+#include "libpspp/zip-reader.h"
#include "output/chart.h"
#include "output/driver.h"
#include "output/page-setup.h"
#define _(msgid) gettext (msgid)
#define N_(msgid) msgid
\f
+const char *
+output_item_type_to_string (enum output_item_type type)
+{
+ switch (type)
+ {
+ case OUTPUT_ITEM_CHART: return "chart";
+ case OUTPUT_ITEM_GROUP: return "group";
+ case OUTPUT_ITEM_IMAGE: return "image";
+ case OUTPUT_ITEM_MESSAGE: return "message";
+ case OUTPUT_ITEM_PAGE_BREAK: return "page break";
+ case OUTPUT_ITEM_PAGE_SETUP: return "page setup";
+ case OUTPUT_ITEM_TABLE: return "table";
+ case OUTPUT_ITEM_TEXT: return "text";
+ }
+
+ NOT_REACHED ();
+}
+\f
#define OUTPUT_ITEM_INITIALIZER(TYPE) .type = TYPE, .ref_cnt = 1, .show = true
/* Increases ITEM's reference count, indicating that it has an additional
chart_unref (item->chart);
break;
- case OUTPUT_ITEM_GROUP_OPEN:
- break;
-
- case OUTPUT_ITEM_GROUP_CLOSE:
+ case OUTPUT_ITEM_GROUP:
+ for (size_t i = 0; i < item->group.n_children; i++)
+ output_item_unref (item->group.children[i]);
break;
case OUTPUT_ITEM_IMAGE:
free (item->label);
free (item->command_name);
free (item->cached_label);
+ spv_info_destroy (item->spv_info);
free (item);
}
}
return item->ref_cnt > 1;
}
-struct output_item *
-output_item_unshare (struct output_item *old)
+/* Returns a clone of OLD, without initializing type-specific fields. */
+static struct output_item *
+output_item_clone_common (const struct output_item *old)
{
- assert (old->ref_cnt > 0);
- if (!output_item_is_shared (old))
- return old;
- output_item_unref (old);
-
struct output_item *new = xmalloc (sizeof *new);
*new = (struct output_item) {
.ref_cnt = 1,
.command_name = xstrdup_if_nonnull (old->command_name),
.type = old->type,
.show = old->show,
+ .spv_info = spv_info_clone (old->spv_info),
};
+ return new;
+}
+
+struct output_item *
+output_item_unshare (struct output_item *old)
+{
+ assert (old->ref_cnt > 0);
+ if (!output_item_is_shared (old))
+ return old;
+ output_item_unref (old);
+
+ struct output_item *new = output_item_clone_common (old);
switch (old->type)
{
case OUTPUT_ITEM_CHART:
new->chart = chart_ref (old->chart);
break;
- case OUTPUT_ITEM_GROUP_OPEN:
- break;
+ case OUTPUT_ITEM_GROUP:
+ new->group.children = xmemdup (
+ old->group.children,
+ old->group.n_children * sizeof *old->group.children);
+ new->group.n_children = new->group.allocated_children
+ = old->group.n_children;
- case OUTPUT_ITEM_GROUP_CLOSE:
+ for (size_t i = 0; i < new->group.n_children; i++)
+ output_item_ref (new->group.children[i]);
break;
case OUTPUT_ITEM_IMAGE:
output_submit (item);
}
+/* If ROOT is a group item, submits each of its children, but not ROOT itself.
+ This is useful if ROOT is being used as a container for output items but it
+ has no significance itself.
+
+ If ROOT is not a group, submits it the normal way.
+
+ Takes ownership of ROOT. */
+void
+output_item_submit_children (struct output_item *root)
+{
+ assert (!output_item_is_shared (root));
+ if (root->type == OUTPUT_ITEM_GROUP)
+ {
+ for (size_t i = 0; i < root->group.n_children; i++)
+ output_submit (root->group.children[i]);
+ root->group.n_children = 0;
+ output_item_unref (root);
+ }
+ else
+ output_submit (root);
+}
+
/* Returns the label for ITEM, which the caller must not modify or free. */
const char *
output_item_get_label (const struct output_item *item)
case OUTPUT_ITEM_CHART:
return item->chart->title ? item->chart->title : _("Chart");
- case OUTPUT_ITEM_GROUP_OPEN:
+ case OUTPUT_ITEM_GROUP:
return item->command_name ? item->command_name : _("Group");
- case OUTPUT_ITEM_GROUP_CLOSE:
- /* Not marked for translation: user should never see it. */
- return "Group Close";
-
case OUTPUT_ITEM_IMAGE:
return "Image";
free (item->label);
item->label = label;
}
+
+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_PAGE_SETUP:
+ printf ("page setup\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);
+ }
+}
\f
struct output_item *
chart_item_create (struct chart *chart)
}
\f
struct output_item *
-group_open_item_create (const char *command_name, const char *label)
+group_item_create (const char *command_name, const char *label)
{
- return group_open_item_create_nocopy (
+ return group_item_create_nocopy (
xstrdup_if_nonnull (command_name),
xstrdup_if_nonnull (label));
}
struct output_item *
-group_open_item_create_nocopy (char *command_name, char *label)
+group_item_create_nocopy (char *command_name, char *label)
{
struct output_item *item = xmalloc (sizeof *item);
*item = (struct output_item) {
- OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_GROUP_OPEN),
+ OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_GROUP),
.label = label,
.command_name = command_name,
};
return item;
}
+/* Returns a new group item suitable as the root node of an output document. A
+ root node is a group whose own properties are mostly disregarded. Instead
+ of having root nodes, it would make just as much sense to just keep around
+ arrays of nodes that would serve as the top level of an output document, but
+ we'd need more special cases instead of just using the existing support for
+ group items. */
struct output_item *
-group_close_item_create (void)
+root_item_create (void)
{
- struct output_item *item = xmalloc (sizeof *item);
- *item = (struct output_item) {
- OUTPUT_ITEM_INITIALIZER (OUTPUT_ITEM_GROUP_CLOSE),
- };
- return item;
+ return group_item_create ("", _("Output"));
+}
+
+/* Returns a clone of OLD but without any of its children. */
+struct output_item *
+group_item_clone_empty (const struct output_item *old)
+{
+ return output_item_clone_common (old);
+}
+
+/* Adds CHILD as a child of group item PARENT. */
+void
+group_item_add_child (struct output_item *parent, struct output_item *child)
+{
+ assert (parent->type == OUTPUT_ITEM_GROUP);
+ assert (!output_item_is_shared (parent));
+ if (parent->group.n_children >= parent->group.allocated_children)
+ parent->group.children = x2nrealloc (parent->group.children,
+ &parent->group.allocated_children,
+ sizeof *parent->group.children);
+ parent->group.children[parent->group.n_children++] = child;
}
\f
/* Creates and returns a new output item containing IMAGE. Takes ownership of
return _("Text");
}
}
+\f
+void
+spv_info_destroy (struct spv_info *spv_info)
+{
+ if (spv_info)
+ {
+ zip_reader_unref (spv_info->zip_reader);
+ free (spv_info->structure_member);
+ free (spv_info->xml_member);
+ free (spv_info->bin_member);
+ free (spv_info->png_member);
+ free (spv_info);
+ }
+}
+struct spv_info *
+spv_info_clone (const struct spv_info *old)
+{
+ if (!old)
+ return NULL;
+
+ struct spv_info *new = xmalloc (sizeof *new);
+ *new = (struct spv_info) {
+ .zip_reader = old->zip_reader ? zip_reader_ref (old->zip_reader) : NULL,
+ .error = old->error,
+ .structure_member = xstrdup_if_nonnull (old->structure_member),
+ .xml_member = xstrdup_if_nonnull (old->xml_member),
+ .bin_member = xstrdup_if_nonnull (old->bin_member),
+ .png_member = xstrdup_if_nonnull (old->png_member),
+ };
+ return new;
+}
+
+size_t
+spv_info_get_members (const struct spv_info *spv_info, const char **members,
+ size_t allocated_members)
+{
+ if (!spv_info)
+ return 0;
+
+ const char *s[] = {
+ spv_info->structure_member,
+ spv_info->xml_member,
+ spv_info->bin_member,
+ spv_info->png_member,
+ };
+ size_t n = 0;
+ for (size_t i = 0; i < sizeof s / sizeof *s; i++)
+ if (s[i] && n < allocated_members)
+ members[n++] = s[i];
+ return n;
+}
#include <cairo.h>
#include <stdbool.h>
#include "libpspp/cast.h"
+#include "libpspp/string-array.h"
enum output_item_type
{
OUTPUT_ITEM_CHART,
- OUTPUT_ITEM_GROUP_OPEN,
- OUTPUT_ITEM_GROUP_CLOSE,
+ OUTPUT_ITEM_GROUP,
OUTPUT_ITEM_IMAGE,
OUTPUT_ITEM_MESSAGE,
OUTPUT_ITEM_PAGE_BREAK,
OUTPUT_ITEM_TEXT,
};
+const char *output_item_type_to_string (enum output_item_type);
+
/* A single output item. */
struct output_item
{
output. */
char *command_name;
- /* For OUTPUT_ITEM_GROUP_OPEN, this is true if the group's subtree should
+ /* For OUTPUT_ITEM_GROUP, this is true if the group's subtree should
be expanded in an outline view, false otherwise.
For other kinds of output items, this is true to show the item's
outline view. */
bool show;
+ /* Information about the SPV file this output_item was read from.
+ May be NULL. */
+ struct spv_info *spv_info;
+
enum output_item_type type;
union
{
cairo_surface_t *image;
+ struct
+ {
+ struct output_item **children;
+ size_t n_children;
+ size_t allocated_children;
+ }
+ group;
+
struct msg *message;
struct page_setup *page_setup;
struct output_item *output_item_unshare (struct output_item *);
void output_item_submit (struct output_item *);
+void output_item_submit_children (struct output_item *);
const char *output_item_get_label (const struct output_item *);
void output_item_set_label (struct output_item *, const char *);
void output_item_set_label_nocopy (struct output_item *, char *);
+
+void output_item_set_command_name (struct output_item *, const char *);
+void output_item_set_command_name_nocopy (struct output_item *, char *);
+
+char *output_item_get_subtype (const struct output_item *);
+
+void output_item_add_spv_info (struct output_item *);
+
+void output_item_dump (const struct output_item *, int indentation);
+\f
+/* In-order traversal of a tree of output items. */
+
+struct output_iterator_node
+ {
+ const struct output_item *group;
+ size_t idx;
+ };
+
+struct output_iterator
+ {
+ const struct output_item *cur;
+ struct output_iterator_node *nodes;
+ size_t n, allocated;
+ };
+#define OUTPUT_ITERATOR_INIT(ITEM) { .cur = ITEM }
+
+/* Iteration functions. */
+void output_iterator_init (struct output_iterator *,
+ const struct output_item *);
+void output_iterator_destroy (struct output_iterator *);
+void output_iterator_next (struct output_iterator *);
+
+/* Iteration helper macros. */
+#define OUTPUT_ITEM_FOR_EACH(ITER, ROOT) \
+ for (output_iterator_init (ITER, ROOT); (ITER)->cur; \
+ output_iterator_next (ITER))
+#define OUTPUT_ITEM_FOR_EACH_SKIP_ROOT(ITER, ROOT) \
+ for (output_iterator_init (ITER, ROOT), output_iterator_next (ITER); \
+ (ITER)->cur; output_iterator_next (ITER))
\f
/* OUTPUT_ITEM_CHART. */
struct output_item *chart_item_create (struct chart *);
\f
-/* OUTPUT_ITEM_GROUP_OPEN. */
-struct output_item *group_open_item_create (const char *command_name,
- const char *label);
-struct output_item *group_open_item_create_nocopy (char *command_name,
- char *label);
-\f
-/* OUTPUT_ITEM_GROUP_CLOSE. */
+/* OUTPUT_ITEM_GROUP. */
+struct output_item *group_item_create (const char *command_name,
+ const char *label);
+struct output_item *group_item_create_nocopy (char *command_name, char *label);
-struct output_item *group_close_item_create (void);
+void group_item_add_child (struct output_item *parent,
+ struct output_item *child);
+
+struct output_item *root_item_create (void);
+
+struct output_item *group_item_clone_empty (const struct output_item *);
\f
/* OUTPUT_ITEM_IMAGE. */
struct output_item *text_item_to_table_item (struct output_item *);
const char *text_item_subtype_to_string (enum text_item_subtype);
+\f
+/* An informational node for output items that were read from an .spv file.
+ This is mostly for debugging and troubleshooting purposes with the
+ pspp-output program. */
+struct spv_info
+ {
+ /* The .spv file. */
+ struct zip_reader *zip_reader;
+
+ /* True if there was an error reading the output item (e.g. because of
+ corruption or because PSPP doesn't understand the format.) */
+ bool error;
+
+ /* Zip member names. All may be NULL. */
+ char *structure_member;
+ char *xml_member;
+ char *bin_member;
+ char *png_member;
+ };
+
+void spv_info_destroy (struct spv_info *);
+struct spv_info *spv_info_clone (const struct spv_info *);
+size_t spv_info_get_members (const struct spv_info *, const char **members,
+ size_t allocated_members);
#endif /* output/output-item.h */
--- /dev/null
+/* PSPP - a program for statistical analysis.
+ Copyright (C) 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 <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include "output/select.h"
+
+#include <string.h>
+
+#include "libpspp/assertion.h"
+#include "libpspp/bit-vector.h"
+#include "libpspp/message.h"
+
+#include "gl/c-ctype.h"
+#include "gl/xalloc.h"
+
+const char *
+output_item_class_to_string (enum output_item_class class)
+{
+ switch (class)
+ {
+#define OUTPUT_CLASS(ENUM, NAME) case OUTPUT_CLASS_##ENUM: return NAME;
+ OUTPUT_CLASSES
+#undef OUTPUT_CLASS
+ default: return NULL;
+ }
+}
+
+enum output_item_class
+output_item_class_from_string (const char *name)
+{
+#define OUTPUT_CLASS(ENUM, NAME) \
+ if (!strcmp (name, NAME)) return OUTPUT_CLASS_##ENUM;
+ OUTPUT_CLASSES
+#undef OUTPUT_CLASS
+
+ return (enum output_item_class) OUTPUT_N_CLASSES;
+}
+
+enum output_item_class
+output_item_classify (const struct output_item *item)
+{
+ const char *label = output_item_get_label (item);
+ if (!label)
+ label = "";
+
+ switch (item->type)
+ {
+ case OUTPUT_ITEM_CHART:
+ return OUTPUT_CLASS_CHARTS;
+
+ case OUTPUT_ITEM_GROUP:
+ return OUTPUT_CLASS_OUTLINEHEADERS;
+
+ case OUTPUT_ITEM_IMAGE:
+ return OUTPUT_CLASS_OTHER;
+
+ case OUTPUT_ITEM_MESSAGE:
+ return (item->message->severity == MSG_S_NOTE
+ ? OUTPUT_CLASS_NOTES
+ : OUTPUT_CLASS_WARNINGS);
+
+ case OUTPUT_ITEM_PAGE_BREAK:
+ return OUTPUT_CLASS_OTHER;
+
+ case OUTPUT_ITEM_PAGE_SETUP:
+ return OUTPUT_CLASS_OTHER;
+
+ case OUTPUT_ITEM_TABLE:
+ return (!strcmp (label, "Warnings") ? OUTPUT_CLASS_WARNINGS
+ : !strcmp (label, "Notes") ? OUTPUT_CLASS_NOTES
+ : OUTPUT_CLASS_TABLES);
+
+ case OUTPUT_ITEM_TEXT:
+ return (!strcmp (label, "Title") ? OUTPUT_CLASS_HEADINGS
+ : !strcmp (label, "Log") ? OUTPUT_CLASS_LOGS
+ : !strcmp (label, "Page Title") ? OUTPUT_CLASS_PAGETITLE
+ : OUTPUT_CLASS_TEXTS);
+
+ default:
+ return OUTPUT_CLASS_UNKNOWN;
+ }
+}
+\f
+static bool
+string_matches (const char *pattern, const char *s)
+{
+ /* XXX This should be a Unicode case insensitive comparison. */
+ while (c_tolower (*pattern) == c_tolower (*s))
+ {
+ if (*pattern == '\0')
+ return true;
+
+ pattern++;
+ s++;
+ }
+
+ return pattern[0] == '*' && pattern[1] == '\0';
+}
+
+static int
+string_array_matches (const char *name, const struct string_array *array)
+{
+ if (!array->n)
+ return -1;
+ else if (!name)
+ return false;
+
+ for (size_t i = 0; i < array->n; i++)
+ if (string_matches (array->strings[i], name))
+ return true;
+
+ return false;
+}
+
+static bool
+match (const char *name,
+ const struct string_array *white,
+ const struct string_array *black)
+{
+ return (string_array_matches (name, white) != false
+ && string_array_matches (name, black) != true);
+}
+
+static int
+match_instance (const int *instances, size_t n_instances,
+ int instance_within_command)
+{
+ int retval = false;
+ for (size_t i = 0; i < n_instances; i++)
+ {
+ if (instances[i] == instance_within_command)
+ return true;
+ else if (instances[i] == -1)
+ retval = -1;
+ }
+ return retval;
+}
+
+static bool
+match_command (size_t nth_command, size_t *commands, size_t n_commands)
+{
+ for (size_t i = 0; i < n_commands; i++)
+ if (nth_command == commands[i])
+ return true;
+ return false;
+}
+
+static void
+select_matches (const struct output_item **items,
+ unsigned int *depths, size_t n_items,
+ const struct output_criteria *c, unsigned long int *include)
+{
+ /* Counting instances within a command. */
+ int instance_within_command = 0;
+ int last_instance = -1;
+
+ /* Counting commands. */
+ const struct output_item *command_item = NULL;
+ const struct output_item *command_command_item = NULL;
+ size_t nth_command = 0;
+
+ for (size_t i = 0; i < n_items; i++)
+ {
+ const struct output_item *item = items[i];
+
+ if (depths[i] == 0)
+ {
+ command_item = item;
+ if (last_instance >= 0)
+ {
+ bitvector_set1 (include, last_instance);
+ last_instance = -1;
+ }
+ instance_within_command = 0;
+ }
+
+ if (!((1u << output_item_classify (item)) & c->classes))
+ continue;
+
+ if (!c->include_hidden && item->type != OUTPUT_ITEM_GROUP && !item->show)
+ continue;
+
+ if (c->error && (!item->spv_info || !item->spv_info->error))
+ continue;
+
+ if (!match (item->command_name,
+ &c->include.commands, &c->exclude.commands))
+ continue;
+
+ if (c->n_commands)
+ {
+ if (command_item != command_command_item)
+ {
+ command_command_item = command_item;
+ nth_command++;
+ }
+
+ if (!match_command (nth_command, c->commands, c->n_commands))
+ continue;
+ }
+
+ char *subtype = output_item_get_subtype (item);
+ bool match_subtype = match (subtype,
+ &c->include.subtypes, &c->exclude.subtypes);
+ if (!match_subtype)
+ continue;
+
+ if (!match (output_item_get_label (item),
+ &c->include.labels, &c->exclude.labels))
+ continue;
+
+ if (c->members.n)
+ {
+ const char *members[4];
+ size_t n = spv_info_get_members (item->spv_info, members,
+ sizeof members / sizeof *members);
+
+ bool found = false;
+ for (size_t j = 0; j < n; j++)
+ if (string_array_matches (members[j], &c->members) == true)
+ {
+ found = true;
+ break;
+ }
+ if (!found)
+ continue;
+ }
+
+ if (c->n_instances)
+ {
+ if (!depths[i])
+ continue;
+ instance_within_command++;
+
+ int include_instance = match_instance (c->instances, c->n_instances,
+ instance_within_command);
+ if (!include_instance)
+ continue;
+ else if (include_instance < 0)
+ {
+ last_instance = i;
+ continue;
+ }
+ }
+
+ bitvector_set1 (include, i);
+ }
+
+ if (last_instance >= 0)
+ bitvector_set1 (include, last_instance);
+}
+
+static size_t
+count_items (const struct output_item *item)
+{
+ size_t n = 1;
+ if (item->type == OUTPUT_ITEM_GROUP)
+ for (size_t i = 0; i < item->group.n_children; i++)
+ n += count_items (item->group.children[i]);
+ return n;
+}
+
+static size_t
+flatten_items (const struct output_item *item, size_t index, size_t depth,
+ const struct output_item **items, unsigned int *depths)
+{
+ items[index] = item;
+ depths[index] = depth;
+ index++;
+
+ if (item->type == OUTPUT_ITEM_GROUP)
+ for (size_t i = 0; i < item->group.n_children; i++)
+ index = flatten_items (item->group.children[i], index, depth + 1,
+ items, depths);
+
+ return index;
+}
+
+static size_t
+unflatten_items (const struct output_item *in, size_t index,
+ unsigned long *include, struct output_item *out)
+{
+ bool include_item = bitvector_is_set (include, index++);
+ if (in->type == OUTPUT_ITEM_GROUP)
+ {
+ /* If we should include the group itself, then clone IN inside OUT, and
+ add any children to the clone instead to OUT directly. */
+ if (include_item)
+ {
+ struct output_item *group = group_item_clone_empty (in);
+ group_item_add_child (out, group);
+ out = group;
+ }
+
+ for (size_t i = 0; i < in->group.n_children; i++)
+ index = unflatten_items (in->group.children[i], index, include, out);
+ }
+ else
+ {
+ if (include_item)
+ group_item_add_child (out, output_item_ref (in));
+ }
+ return index;
+}
+
+/* Consumes IN, which must be a group, and returns a new output item whose
+ children are all the (direct and indirect) children of IN that meet the NC
+ criteria in C[]. */
+struct output_item *
+output_select (struct output_item *in,
+ const struct output_criteria c[], size_t nc)
+{
+ assert (in->type == OUTPUT_ITEM_GROUP);
+ if (!nc)
+ return in;
+
+ /* Number of items (not counting the root). */
+ size_t n_items = count_items (in) - 1;
+
+ const struct output_item **items = xnmalloc (n_items, sizeof *items);
+ unsigned int *depths = xnmalloc (n_items, sizeof *depths);
+ size_t n_flattened = 0;
+ for (size_t i = 0; i < in->group.n_children; i++)
+ n_flattened = flatten_items (in->group.children[i], n_flattened,
+ 0, items, depths);
+ assert (n_flattened == n_items);
+
+ unsigned long int *include = bitvector_allocate (n_items);
+ for (size_t i = 0; i < nc; i++)
+ select_matches (items, depths, n_items, &c[i], include);
+
+ struct output_item *out = root_item_create ();
+ size_t n_unflattened = 0;
+ for (size_t i = 0; i < in->group.n_children; i++)
+ n_unflattened = unflatten_items (in->group.children[i], n_unflattened,
+ include, out);
+ assert (n_unflattened == n_items);
+
+ free (items);
+ free (depths);
+ free (include);
+
+ output_item_unref (in);
+ return out;
+}
--- /dev/null
+/* PSPP - a program for statistical analysis.
+ Copyright (C) 2009, 2011 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_SELECT_H
+#define OUTPUT_SELECT_H 1
+
+#include "output/output-item.h"
+
+/* Selecting subsets of a tree of output items based on user-specified
+ criteria. pspp-output uses this; a future OMS or OUTPUT MODIFY command
+ would use it too. */
+
+/* Classifications for output items. These only roughly correspond to the
+ output item types; for example, "warnings" are a subset of text items.
+ These classifications really */
+#define OUTPUT_CLASSES \
+ OUTPUT_CLASS(CHARTS, "charts") \
+ OUTPUT_CLASS(HEADINGS, "headings") \
+ OUTPUT_CLASS(LOGS, "logs") \
+ OUTPUT_CLASS(MODELS, "models") \
+ OUTPUT_CLASS(TABLES, "tables") \
+ OUTPUT_CLASS(TEXTS, "texts") \
+ OUTPUT_CLASS(TREES, "trees") \
+ OUTPUT_CLASS(WARNINGS, "warnings") \
+ OUTPUT_CLASS(OUTLINEHEADERS, "outlineheaders") \
+ OUTPUT_CLASS(PAGETITLE, "pagetitle") \
+ OUTPUT_CLASS(NOTES, "notes") \
+ OUTPUT_CLASS(UNKNOWN, "unknown") \
+ OUTPUT_CLASS(OTHER, "other")
+enum output_item_class
+ {
+#define OUTPUT_CLASS(ENUM, NAME) OUTPUT_CLASS_##ENUM,
+ OUTPUT_CLASSES
+#undef OUTPUT_CLASS
+ };
+enum
+ {
+#define OUTPUT_CLASS(ENUM, NAME) +1
+ OUTPUT_N_CLASSES = OUTPUT_CLASSES
+#undef OUTPUT_CLASS
+};
+#define OUTPUT_ALL_CLASSES ((1u << OUTPUT_N_CLASSES) - 1)
+
+const char *output_item_class_to_string (enum output_item_class);
+enum output_item_class output_item_class_from_string (const char *);
+
+enum output_item_class output_item_classify (const struct output_item *);
+
+/* Matching criteria for commands, subtypes, and labels.
+
+ Each of the members is an array of strings. A string that ends in '*'
+ matches anything that begins with the rest of the string, otherwise a string
+ requires an exact (case-insensitive) match. */
+struct output_criteria_match
+ {
+ struct string_array commands;
+ struct string_array subtypes;
+ struct string_array labels;
+ };
+
+struct output_criteria
+ {
+ /* Include objects that are not visible? */
+ bool include_hidden;
+
+ /* If false, include all objects.
+ If true, include only objects that have an error on loading. */
+ bool error;
+
+ /* Bit-mask of OUTPUT_CLASS_* for the classes to include. */
+ unsigned int classes;
+
+ /* Include all of the objects that match 'include' and do not match
+ 'exclude', except that objects are included by default if 'include' is
+ empty. */
+ struct output_criteria_match include;
+ struct output_criteria_match exclude;
+
+ /* Include objects under commands with indexes listed in COMMANDS. Indexes
+ are 1-based. Everything is included if N_COMMANDS is 0. */
+ size_t *commands;
+ size_t n_commands;
+
+ /* Include XML and binary member names that match (except that everything
+ is included by default if empty). */
+ struct string_array members;
+
+ /* Include the objects with indexes listed in INSTANCES within each of the
+ commands that are included. Indexes are 1-based. Index -1 means the
+ last object within a command. */
+ int *instances;
+ size_t n_instances;
+ };
+
+#define OUTPUT_CRITERIA_INITIALIZER { .classes = OUTPUT_ALL_CLASSES }
+
+struct output_item *output_select (struct output_item *,
+ const struct output_criteria[],
+ size_t n_criteria);
+
+#endif /* output/select.h */
static const struct output_driver_class spv_driver_class =
{
- "spv",
- spv_destroy,
- spv_submit,
- NULL,
+ .name = "spv",
+ .destroy = spv_destroy,
+ .submit = spv_submit,
+ .handles_show = true,
+ .handles_groups = true,
};
src_output_liboutput_la_SOURCES += \
src/output/spv/spv-css-parser.c \
src/output/spv/spv-css-parser.h \
- src/output/spv/spv-dump.c \
src/output/spv/spv-legacy-data.c \
src/output/spv/spv-legacy-data.h \
src/output/spv/spv-legacy-decoder.c \
src/output/spv/spv-legacy-decoder.h \
src/output/spv/spv-light-decoder.c \
src/output/spv/spv-light-decoder.h \
- src/output/spv/spv-output.c \
- src/output/spv/spv-output.h \
- src/output/spv/spv-select.c \
- src/output/spv/spv-select.h \
src/output/spv/spv-table-look.c \
src/output/spv/spv-table-look.h \
src/output/spv/spv-writer.c \
+++ /dev/null
-/* 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 <http://www.gnu.org/licenses/>. */
-
-#include <config.h>
-
-#include "output/spv/spv.h"
-
-#include <inttypes.h>
-#include <stdlib.h>
-
-#include "data/settings.h"
-#include "output/pivot-table.h"
-
-#include "gl/xalloc.h"
-
-static void
-indent (int indentation)
-{
- for (int i = 0; i < indentation * 2; i++)
- putchar (' ');
-}
-
-void
-spv_item_dump (const struct spv_item *item, int indentation)
-{
- indent (indentation);
- if (item->label)
- printf ("\"%s\" ", item->label);
- if (!item->visible)
- printf ("(hidden) ");
-
- switch (item->type)
- {
- case SPV_ITEM_HEADING:
- printf ("heading\n");
- for (size_t i = 0; i < item->n_children; i++)
- spv_item_dump (item->children[i], indentation + 1);
- break;
-
- case SPV_ITEM_TEXT:
- printf ("text \"%s\"\n", pivot_value_to_string_defaults (item->text));
- break;
-
- case SPV_ITEM_TABLE:
- if (item->table)
- pivot_table_dump (item->table, indentation + 1);
- else
- {
- printf ("unloaded table in %s", item->bin_member);
- if (item->xml_member)
- printf (" and %s", item->xml_member);
- putchar ('\n');
- }
- break;
-
- case SPV_ITEM_GRAPH:
- printf ("graph\n");
- break;
-
- case SPV_ITEM_MODEL:
- printf ("model\n");
- break;
-
- case SPV_ITEM_IMAGE:
- printf ("image in %s\n", item->png_member);
- break;
-
- case SPV_ITEM_TREE:
- printf ("tree\n");
- break;
- }
-}
spv_legacy_data_decode (const uint8_t *in, size_t size, struct spv_data *out)
{
char *error = NULL;
- memset (out, 0, sizeof *out);
+ *out = (struct spv_data) SPV_DATA_INITIALIZER;
struct spvbin_input input;
spvbin_input_init (&input, in, size);
error:
spv_data_uninit (out);
- memset (out, 0, sizeof *out);
+ *out = (struct spv_data) SPV_DATA_INITIALIZER;
spvob_free_legacy_binary (lb);
return error;
}
size_t n_sources;
};
+#define SPV_DATA_INITIALIZER { NULL, 0 }
+
void spv_data_uninit (struct spv_data *);
void spv_data_dump (const struct spv_data *, FILE *);
+++ /dev/null
-/* PSPP - a program for statistical analysis.
- Copyright (C) 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 <http://www.gnu.org/licenses/>. */
-
-#include <config.h>
-
-#include "output/spv/spv-output.h"
-
-#include "output/pivot-table.h"
-#include "output/spv/spv.h"
-#include "output/output-item.h"
-
-#include "gl/xalloc.h"
-
-void
-spv_text_submit (const struct spv_item *in)
-{
- enum spv_item_class class = spv_item_get_class (in);
- struct output_item *item = text_item_create_value (
- (class == SPV_CLASS_HEADINGS ? TEXT_ITEM_TITLE
- : class == SPV_CLASS_PAGETITLE ? TEXT_ITEM_PAGE_TITLE
- : TEXT_ITEM_LOG),
- pivot_value_clone (spv_item_get_text (in)),
- xstrdup_if_nonnull (in->label));
- output_item_submit (item);
-}
+++ /dev/null
-/* PSPP - a program for statistical analysis.
- Copyright (C) 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 <http://www.gnu.org/licenses/>. */
-
-#ifndef OUTPUT_SPV_OUTPUT_H
-#define OUTPUT_SPV_OUTPUT_H 1
-
-/* Interface between SPVs and the PSPP output engine. */
-
-struct spv_item;
-
-void spv_text_submit (const struct spv_item *);
-
-#endif /* output/spv/spv-output.h */
+++ /dev/null
-/* PSPP - a program for statistical analysis.
- Copyright (C) 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 <http://www.gnu.org/licenses/>. */
-
-#include <config.h>
-
-#include "spv-select.h"
-
-#include <string.h>
-
-#include "libpspp/assertion.h"
-#include "libpspp/bit-vector.h"
-#include "output/spv/spv.h"
-
-#include "gl/c-ctype.h"
-#include "gl/xalloc.h"
-
-/* Returns true if ITEM represents a command, false otherwise.
-
- The root item and each of its immediate children are considered to be
- command items. */
-static bool
-is_command_item (const struct spv_item *item)
-{
- return !item->parent || !item->parent->parent;
-}
-
-static struct spv_item *
-find_command_item (struct spv_item *item)
-{
- while (!is_command_item (item))
- item = item->parent;
- return item;
-}
-
-static bool
-string_matches (const char *pattern, const char *s)
-{
- /* XXX This should be a Unicode case insensitive comparison. */
- while (c_tolower (*pattern) == c_tolower (*s))
- {
- if (*pattern == '\0')
- return true;
-
- pattern++;
- s++;
- }
-
- return pattern[0] == '*' && pattern[1] == '\0';
-}
-
-static int
-string_array_matches (const char *name, const struct string_array *array)
-{
- if (!array->n)
- return -1;
- else if (!name)
- return false;
-
- for (size_t i = 0; i < array->n; i++)
- if (string_matches (array->strings[i], name))
- return true;
-
- return false;
-}
-
-static bool
-match (const char *name,
- const struct string_array *white,
- const struct string_array *black)
-{
- return (string_array_matches (name, white) != false
- && string_array_matches (name, black) != true);
-}
-
-static int
-match_instance (const int *instances, size_t n_instances,
- int instance_within_command)
-{
- int retval = false;
- for (size_t i = 0; i < n_instances; i++)
- {
- if (instances[i] == instance_within_command)
- return true;
- else if (instances[i] == -1)
- retval = -1;
- }
- return retval;
-}
-
-static bool
-match_command (size_t nth_command, size_t *commands, size_t n_commands)
-{
- for (size_t i = 0; i < n_commands; i++)
- if (nth_command == commands[i])
- return true;
- return false;
-}
-
-static void
-select_matches (const struct spv_reader *spv, const struct spv_criteria *c,
- unsigned long int *include)
-{
- /* Counting instances within a command. */
- struct spv_item *instance_command_item = NULL;
- int instance_within_command = 0;
- int last_instance = -1;
-
- /* Counting commands. */
- struct spv_item *command_command_item = NULL;
- size_t nth_command = 0;
-
- struct spv_item *item;
- ssize_t index = -1;
- SPV_ITEM_FOR_EACH_SKIP_ROOT (item, spv_get_root (spv))
- {
- index++;
-
- struct spv_item *new_command_item = find_command_item (item);
- if (new_command_item != instance_command_item)
- {
- if (last_instance >= 0)
- {
- bitvector_set1 (include, last_instance);
- last_instance = -1;
- }
-
- instance_command_item = new_command_item;
- instance_within_command = 0;
- }
-
- if (!((1u << spv_item_get_class (item)) & c->classes))
- continue;
-
- if (!c->include_hidden && !spv_item_is_visible (item))
- continue;
-
- if (c->error)
- {
- spv_item_load (item);
- if (!item->error)
- continue;
- }
-
- if (!match (spv_item_get_command_id (item),
- &c->include.commands, &c->exclude.commands))
- continue;
-
- if (c->n_commands)
- {
- if (new_command_item != command_command_item)
- {
- command_command_item = new_command_item;
- nth_command++;
- }
-
- if (!match_command (nth_command, c->commands, c->n_commands))
- continue;
- }
-
- if (!match (spv_item_get_subtype (item),
- &c->include.subtypes, &c->exclude.subtypes))
- continue;
-
- if (!match (spv_item_get_label (item),
- &c->include.labels, &c->exclude.labels))
- continue;
-
- if (c->members.n)
- {
- char *members[] = {
- item->structure_member,
- item->xml_member,
- item->bin_member,
- item->png_member
- };
-
- bool found = false;
- for (size_t i = 0; i < sizeof members / sizeof *members; i++)
- if (string_array_matches (members[i], &c->members) == true)
- {
- found = true;
- break;
- }
- if (!found)
- continue;
- }
-
- if (c->n_instances)
- {
- if (is_command_item (item))
- continue;
- instance_within_command++;
-
- int include_instance = match_instance (c->instances, c->n_instances,
- instance_within_command);
- if (!include_instance)
- continue;
- else if (include_instance < 0)
- {
- last_instance = index;
- continue;
- }
- }
-
- bitvector_set1 (include, index);
- }
-
- if (last_instance >= 0)
- bitvector_set1 (include, last_instance);
-}
-
-void
-spv_select (const struct spv_reader *spv,
- const struct spv_criteria c[], size_t nc,
- struct spv_item ***itemsp, size_t *n_itemsp)
-{
- struct spv_item *item;
-
- struct spv_criteria default_criteria = SPV_CRITERIA_INITIALIZER;
- if (!nc)
- {
- nc = 1;
- c = &default_criteria;
- }
-
- /* Count items. */
- size_t max_items = 0;
- SPV_ITEM_FOR_EACH_SKIP_ROOT (item, spv_get_root (spv))
- max_items++;
-
- /* Allocate bitmap for items then fill it in with selected items. */
- unsigned long int *include = bitvector_allocate (max_items);
- for (size_t i = 0; i < nc; i++)
- select_matches (spv, &c[i], include);
-
- /* Copy selected items into output array. */
- size_t n_items = 0;
- struct spv_item **items = xnmalloc (bitvector_count (include, max_items),
- sizeof *items);
- size_t i = 0;
- SPV_ITEM_FOR_EACH_SKIP_ROOT (item, spv_get_root (spv))
- if (bitvector_is_set (include, i++))
- items[n_items++] = item;
- *itemsp = items;
- *n_itemsp = n_items;
-
- /* Free memory. */
- free (include);
-}
+++ /dev/null
-/* PSPP - a program for statistical analysis.
- Copyright (C) 2019 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 <http://www.gnu.org/licenses/>. */
-
-#ifndef OUTPUT_SPV_SELECT_H
-#define OUTPUT_SPV_SELECT_H 1
-
-#include "libpspp/string-array.h"
-
-struct spv_item;
-struct spv_reader;
-
-/* Matching criteria for commands, subtypes, and labels.
-
- Each of the members is an array of strings. A string that ends in '*'
- matches anything that begins with the rest of the string, otherwise a string
- requires an exact (case-insensitive) match. */
-struct spv_criteria_match
- {
- struct string_array commands;
- struct string_array subtypes;
- struct string_array labels;
- };
-
-struct spv_criteria
- {
- /* Include objects that are not visible? */
- bool include_hidden;
-
- /* If false, include all objects.
- If true, include only objects that have an error on loading. */
- bool error;
-
- /* Bit-mask of SPV_CLASS_* for the classes to include. */
- unsigned int classes;
-
- /* Include all of the objects that match 'include' and do not match
- 'exclude', except that objects are included by default if 'include' is
- empty. */
- struct spv_criteria_match include;
- struct spv_criteria_match exclude;
-
- /* Include objects under commands with indexes listed in COMMANDS. Indexes
- are 1-based. Everything is included if N_COMMANDS is 0. */
- size_t *commands;
- size_t n_commands;
-
- /* Include XML and binary member names that match (except that everything
- is included by default if empty). */
- struct string_array members;
-
- /* Include the objects with indexes listed in INSTANCES within each of the
- commands that are included. Indexes are 1-based. Index -1 means the
- last object within a command. */
- int *instances;
- size_t n_instances;
- };
-
-#define SPV_CRITERIA_INITIALIZER { .classes = SPV_ALL_CLASSES }
-
-void spv_select (const struct spv_reader *,
- const struct spv_criteria[], size_t n_criteria,
- struct spv_item ***items, size_t *n_items);
-
-#endif /* output/spv/spv-select.h */
}
break;
- case OUTPUT_ITEM_GROUP_OPEN:
+ case OUTPUT_ITEM_GROUP:
spv_writer_open_heading (w, item);
- break;
-
- case OUTPUT_ITEM_GROUP_CLOSE:
+ for (size_t i = 0; i < item->group.n_children; i++)
+ spv_writer_write (w, item->group.children[i]);
spv_writer_close_heading (w);
break;
#include "output/spv/spv.h"
#include <assert.h>
+#include <cairo.h>
#include <inttypes.h>
#include <libxml/HTMLparser.h>
#include <libxml/xmlreader.h>
#include "libpspp/message.h"
#include "libpspp/str.h"
#include "libpspp/zip-reader.h"
+#include "output/output-item.h"
#include "output/page-setup.h"
#include "output/pivot-table.h"
#include "output/spv/detail-xml-parser.h"
struct page_setup *page_setup;
};
-const struct page_setup *
-spv_get_page_setup (const struct spv_reader *spv)
-{
- return spv->page_setup;
-}
-
-const char *
-spv_item_type_to_string (enum spv_item_type type)
-{
- switch (type)
- {
- case SPV_ITEM_HEADING: return "heading";
- case SPV_ITEM_TEXT: return "text";
- case SPV_ITEM_TABLE: return "table";
- case SPV_ITEM_GRAPH: return "graph";
- case SPV_ITEM_MODEL: return "model";
- case SPV_ITEM_IMAGE: return "image";
- default: return "**error**";
- }
-}
-
-const char *
-spv_item_class_to_string (enum spv_item_class class)
-{
- switch (class)
- {
-#define SPV_CLASS(ENUM, NAME) case SPV_CLASS_##ENUM: return NAME;
- SPV_CLASSES
-#undef SPV_CLASS
- default: return NULL;
- }
-}
-
-enum spv_item_class
-spv_item_class_from_string (const char *name)
-{
-#define SPV_CLASS(ENUM, NAME) \
- if (!strcmp (name, NAME)) return SPV_CLASS_##ENUM;
- SPV_CLASSES
-#undef SPV_CLASS
-
- return (enum spv_item_class) SPV_N_CLASSES;
-}
-
-enum spv_item_type
-spv_item_get_type (const struct spv_item *item)
-{
- return item->type;
-}
-
-enum spv_item_class
-spv_item_get_class (const struct spv_item *item)
-{
- const char *label = spv_item_get_label (item);
- if (!label)
- label = "";
-
- switch (item->type)
- {
- case SPV_ITEM_HEADING:
- return SPV_CLASS_HEADINGS;
-
- case SPV_ITEM_TEXT:
- return (!strcmp (label, "Title") ? SPV_CLASS_OUTLINEHEADERS
- : !strcmp (label, "Log") ? SPV_CLASS_LOGS
- : !strcmp (label, "Page Title") ? SPV_CLASS_PAGETITLE
- : SPV_CLASS_TEXTS);
-
- case SPV_ITEM_TABLE:
- return (!strcmp (label, "Warnings") ? SPV_CLASS_WARNINGS
- : !strcmp (label, "Notes") ? SPV_CLASS_NOTES
- : SPV_CLASS_TABLES);
-
- case SPV_ITEM_GRAPH:
- return SPV_CLASS_CHARTS;
-
- case SPV_ITEM_MODEL:
- return SPV_CLASS_MODELS;
-
- case SPV_ITEM_IMAGE:
- return SPV_CLASS_OTHER;
-
- case SPV_ITEM_TREE:
- return SPV_CLASS_TREES;
-
- default:
- return SPV_CLASS_UNKNOWN;
- }
-}
-
-const char *
-spv_item_get_label (const struct spv_item *item)
-{
- return item->label;
-}
-
-bool
-spv_item_is_heading (const struct spv_item *item)
-{
- return item->type == SPV_ITEM_HEADING;
-}
-
-size_t
-spv_item_get_n_children (const struct spv_item *item)
-{
- return item->n_children;
-}
-
-struct spv_item *
-spv_item_get_child (const struct spv_item *item, size_t idx)
-{
- assert (idx < item->n_children);
- return item->children[idx];
-}
-
-bool
-spv_item_is_table (const struct spv_item *item)
-{
- return item->type == SPV_ITEM_TABLE;
-}
-
-bool
-spv_item_is_text (const struct spv_item *item)
-{
- return item->type == SPV_ITEM_TEXT;
-}
-
-const struct pivot_value *
-spv_item_get_text (const struct spv_item *item)
-{
- assert (spv_item_is_text (item));
- return item->text;
-}
-
-bool
-spv_item_is_image (const struct spv_item *item)
-{
- return item->type == SPV_ITEM_IMAGE;
-}
-
-static cairo_status_t
-read_from_zip_member (void *zm_, unsigned char *data, unsigned int length)
-{
- struct zip_member *zm = zm_;
- if (!zm)
- return CAIRO_STATUS_READ_ERROR;
-
- while (length > 0)
- {
- int n = zip_member_read (zm, data, length);
- if (n <= 0)
- return CAIRO_STATUS_READ_ERROR;
-
- data += n;
- length -= n;
- }
-
- return CAIRO_STATUS_SUCCESS;
-}
-
-cairo_surface_t *
-spv_item_get_image (const struct spv_item *item_)
-{
- struct spv_item *item = CONST_CAST (struct spv_item *, item_);
- assert (spv_item_is_image (item));
-
- if (!item->image)
- {
- struct zip_member *zm;
- char *error = zip_member_open (item->spv->zip, item->png_member, &zm);
- item->image = cairo_image_surface_create_from_png_stream (
- read_from_zip_member, zm);
- zip_member_finish (zm);
- free (error);
- }
-
- return item->image;
-}
-
-struct spv_item *
-spv_item_next (const struct spv_item *item)
-{
- if (item->n_children)
- return item->children[0];
-
- while (item->parent)
- {
- size_t idx = item->parent_idx + 1;
- item = item->parent;
- if (idx < item->n_children)
- return item->children[idx];
- }
-
- return NULL;
-}
-
-const struct spv_item *
-spv_item_get_parent (const struct spv_item *item)
-{
- return item->parent;
-}
-
-size_t
-spv_item_get_level (const struct spv_item *item)
-{
- int level = 0;
- for (; item->parent; item = item->parent)
- level++;
- return level;
-}
-
-const char *
-spv_item_get_command_id (const struct spv_item *item)
-{
- return item->command_id;
-}
-
-const char *
-spv_item_get_subtype (const struct spv_item *item)
-{
- return item->subtype;
-}
-
-bool
-spv_item_is_visible (const struct spv_item *item)
-{
- return item->visible;
-}
-
-static void
-spv_item_destroy (struct spv_item *item)
-{
- if (item)
- {
- free (item->structure_member);
-
- free (item->label);
- free (item->command_id);
-
- for (size_t i = 0; i < item->n_children; i++)
- spv_item_destroy (item->children[i]);
- free (item->children);
-
- pivot_table_unref (item->table);
- pivot_table_look_unref (item->table_look);
- free (item->bin_member);
- free (item->xml_member);
- free (item->subtype);
-
- pivot_value_destroy (item->text);
-
- free (item->png_member);
- if (item->image)
- cairo_surface_destroy (item->image);
-
- free (item);
- }
-}
-
-static void
-spv_heading_add_child (struct spv_item *parent, struct spv_item *child)
-{
- assert (parent->type == SPV_ITEM_HEADING);
- assert (!child->parent);
-
- child->parent = parent;
- child->parent_idx = parent->n_children;
-
- if (parent->n_children >= parent->allocated_children)
- parent->children = x2nrealloc (parent->children,
- &parent->allocated_children,
- sizeof *parent->children);
- parent->children[parent->n_children++] = child;
-}
-
static xmlNode *
find_xml_child_element (xmlNode *parent, const char *child_name)
{
return ds_steal_cstr (&markup);
}
-static char *
-xstrdup_if_nonempty (const char *s)
-{
- return s && s[0] ? xstrdup (s) : NULL;
-}
+static struct output_item *
+decode_container_text (const struct spvsx_container_text *ct)
+{
+ struct font_style *font_style = xmalloc (sizeof *font_style);
+ char *text = decode_embedded_html (ct->html->node_.raw, font_style);
+ struct pivot_value *value = xmalloc (sizeof *value);
+ *value = (struct pivot_value) {
+ .font_style = font_style,
+ .type = PIVOT_VALUE_TEXT,
+ .text = {
+ .local = text,
+ .c = text,
+ .id = text,
+ .user_provided = true,
+ },
+ };
-static void
-decode_container_text (const struct spvsx_container_text *ct,
- struct spv_item *item)
-{
- item->type = SPV_ITEM_TEXT;
- item->command_id = xstrdup_if_nonempty (ct->command_name);
-
- item->text = xzalloc (sizeof *item->text);
- item->text->type = PIVOT_VALUE_TEXT;
- item->text->font_style = xmalloc (sizeof *item->text->font_style);
- item->text->text.local = decode_embedded_html (ct->html->node_.raw,
- item->text->font_style);
+ struct output_item *item = text_item_create_value (TEXT_ITEM_LOG,
+ value, NULL);
+ output_item_set_command_name (item, ct->command_name);
+ return item;
}
static void
xmlFreeDoc (html_doc);
}
-void
-spv_item_load (const struct spv_item *item)
-{
- if (spv_item_is_table (item))
- spv_item_get_table (item);
- else if (spv_item_is_image (item))
- spv_item_get_image (item);
-}
-
-bool
-spv_item_is_light_table (const struct spv_item *item)
-{
- return item->type == SPV_ITEM_TABLE && !item->xml_member;
-}
-
char * WARN_UNUSED_RESULT
-spv_item_get_raw_light_table (const struct spv_item *item,
- void **data, size_t *size)
-{
- return zip_member_read_all (item->spv->zip, item->bin_member, data, size);
-}
-
-char * WARN_UNUSED_RESULT
-spv_item_get_light_table (const struct spv_item *item,
- struct spvlb_table **tablep)
+spv_read_light_table (struct zip_reader *zip, const char *bin_member,
+ struct spvlb_table **tablep)
{
*tablep = NULL;
- if (!spv_item_is_light_table (item))
- return xstrdup ("not a light binary table object");
-
void *data;
size_t size;
- char *error = spv_item_get_raw_light_table (item, &data, &size);
+ char *error = zip_member_read_all (zip, bin_member, &data, &size);
if (error)
return error;
: input.ofs != input.size
? xasprintf ("expected end of file at offset %#zx", input.ofs)
: NULL);
- if (error)
- {
- struct string s = DS_EMPTY_INITIALIZER;
- spv_item_format_path (item, &s);
- ds_put_format (&s, " (%s): %s", item->bin_member, error);
-
- free (error);
- error = ds_steal_cstr (&s);
- }
free (data);
if (!error)
*tablep = table;
return error;
}
-static char *
-pivot_table_open_light (struct spv_item *item)
+static char * WARN_UNUSED_RESULT
+pivot_table_open_light (struct zip_reader *zip, const char *bin_member,
+ struct pivot_table **tablep)
{
- assert (spv_item_is_light_table (item));
+ *tablep = NULL;
struct spvlb_table *raw_table;
- char *error = spv_item_get_light_table (item, &raw_table);
+ char *error = spv_read_light_table (zip, bin_member, &raw_table);
if (!error)
- error = decode_spvlb_table (raw_table, &item->table);
+ error = decode_spvlb_table (raw_table, tablep);
spvlb_free_table (raw_table);
return error;
}
-bool
-spv_item_is_legacy_table (const struct spv_item *item)
-{
- return item->type == SPV_ITEM_TABLE && item->xml_member;
-}
-
char * WARN_UNUSED_RESULT
-spv_item_get_raw_legacy_data (const struct spv_item *item,
- void **data, size_t *size)
-{
- if (!spv_item_is_legacy_table (item))
- return xstrdup ("not a legacy table object");
-
- return zip_member_read_all (item->spv->zip, item->bin_member, data, size);
-}
-
-char * WARN_UNUSED_RESULT
-spv_item_get_legacy_data (const struct spv_item *item, struct spv_data *data)
+spv_read_legacy_data (struct zip_reader *zip, const char *bin_member,
+ struct spv_data *data)
{
void *raw;
size_t size;
- char *error = spv_item_get_raw_legacy_data (item, &raw, &size);
+ char *error = zip_member_read_all (zip, bin_member, &raw, &size);
if (!error)
{
error = spv_legacy_data_decode (raw, size, data);
return error;
}
-static char * WARN_UNUSED_RESULT
-spv_read_xml_member (struct spv_reader *spv, const char *member_name,
+char * WARN_UNUSED_RESULT
+spv_read_xml_member (struct zip_reader *zip, const char *xml_member,
bool keep_blanks, const char *root_element_name,
xmlDoc **docp)
{
*docp = NULL;
struct zip_member *zm;
- char *error = zip_member_open (spv->zip, member_name, &zm);
+ char *error = zip_member_open (zip, xml_member, &zm);
if (error)
return error;
if (!parser)
{
zip_member_finish (zm);
- return xasprintf (_("%s: Failed to create XML parser"), member_name);
+ return xasprintf (_("%s: Failed to create XML parser"), xml_member);
}
int retval;
if (!well_formed)
{
xmlFreeDoc (doc);
- return xasprintf(_("%s: document is not well-formed"), member_name);
+ return xasprintf(_("%s: document is not well-formed"), xml_member);
}
const xmlNode *root_node = xmlDocGetRootElement (doc);
{
xmlFreeDoc (doc);
return xasprintf(_("%s: root node is \"%s\" but \"%s\" was expected"),
- member_name,
+ xml_member,
CHAR_CAST (char *, root_node->name), root_element_name);
}
return NULL;
}
-char * WARN_UNUSED_RESULT
-spv_item_get_legacy_table (const struct spv_item *item, xmlDoc **docp)
-{
- assert (spv_item_is_legacy_table (item));
-
- return spv_read_xml_member (item->spv, item->xml_member, false,
- "visualization", docp);
-}
-
-char * WARN_UNUSED_RESULT
-spv_item_get_structure (const struct spv_item *item, struct _xmlDoc **docp)
-{
- return spv_read_xml_member (item->spv, item->structure_member, false,
- "heading", docp);
-}
-
+#if 0
static const char *
-identify_item (const struct spv_item *item)
+identify_item (const struct output_item *item)
{
return (item->label ? item->label
: item->command_id ? item->command_id
}
}
}
+#endif
static char * WARN_UNUSED_RESULT
-pivot_table_open_legacy (struct spv_item *item)
+pivot_table_open_legacy (struct zip_reader *zip, const char *bin_member,
+ const char *xml_member, const char *subtype,
+ const struct pivot_table_look *look,
+ struct pivot_table **tablep)
{
- assert (spv_item_is_legacy_table (item));
+ *tablep = NULL;
- struct spv_data data;
- char *error = spv_item_get_legacy_data (item, &data);
+ struct spv_data data = SPV_DATA_INITIALIZER;
+ char *error = spv_read_legacy_data (zip, bin_member, &data);
if (error)
- {
- struct string s = DS_EMPTY_INITIALIZER;
- spv_item_format_path (item, &s);
- ds_put_format (&s, " (%s): %s", item->bin_member, error);
-
- free (error);
- return ds_steal_cstr (&s);
- }
+ goto exit;
xmlDoc *doc;
- error = spv_read_xml_member (item->spv, item->xml_member, false,
+ error = spv_read_xml_member (zip, xml_member, false,
"visualization", &doc);
if (error)
- {
- spv_data_uninit (&data);
- return error;
- }
+ goto exit_free_data;
struct spvxml_context ctx = SPVXML_CONTEXT_INIT (ctx);
struct spvdx_visualization *v;
spvdx_parse_visualization (&ctx, xmlDocGetRootElement (doc), &v);
error = spvxml_context_finish (&ctx, &v->node_);
-
- if (!error)
- error = decode_spvdx_table (v, item->subtype, item->table_look,
- &data, &item->table);
-
if (error)
- {
- struct string s = DS_EMPTY_INITIALIZER;
- spv_item_format_path (item, &s);
- ds_put_format (&s, " (%s): %s", item->xml_member, error);
+ goto exit_free_doc;
- free (error);
- error = ds_steal_cstr (&s);
- }
+ error = decode_spvdx_table (v, subtype, look, &data, tablep);
- spv_data_uninit (&data);
spvdx_free_visualization (v);
+exit_free_doc:
if (doc)
xmlFreeDoc (doc);
-
+exit_free_data:
+ spv_data_uninit (&data);
+exit:
return error;
}
-const struct pivot_table *
-spv_item_get_table (const struct spv_item *item_)
+static struct output_item *
+spv_read_table_item (struct zip_reader *zip,
+ const struct spvsx_table *table)
{
- struct spv_item *item = CONST_CAST (struct spv_item *, item_);
+ const struct spvsx_table_structure *ts = table->table_structure;
+ const char *bin_member = ts->data_path->text;
+ const char *xml_member = ts->path ? ts->path->text : NULL;
- assert (spv_item_is_table (item));
- if (!item->table)
+ struct pivot_table *pt = NULL;
+ char *error;
+ if (xml_member)
{
- char *error = (item->xml_member
- ? pivot_table_open_legacy (item)
- : pivot_table_open_light (item));
- if (error)
+ struct pivot_table_look *look;
+ error = (table->table_properties
+ ? spv_table_look_decode (table->table_properties, &look)
+ : xstrdup ("Legacy table lacks tableProperties"));
+ if (!error)
{
- item->error = true;
- msg (ME, "%s", error);
- item->table = pivot_table_create_for_text (
- pivot_value_new_text (N_("Error")),
- pivot_value_new_user_text (error, -1));
- free (error);
+ error = pivot_table_open_legacy (zip, bin_member, xml_member,
+ table->sub_type, look, &pt);
+ pivot_table_look_unref (look);
}
}
+ else
+ error = pivot_table_open_light (zip, bin_member, &pt);
+ if (error)
+ pt = pivot_table_create_for_text (
+ pivot_value_new_text (N_("Error")),
+ pivot_value_new_user_text_nocopy (error));
- return item->table;
+ struct output_item *item = table_item_create (pt);
+ output_item_set_command_name (item, table->command_name);
+ output_item_add_spv_info (item);
+ item->spv_info->error = error != NULL;
+ item->spv_info->zip_reader = zip_reader_ref (zip);
+ item->spv_info->bin_member = xstrdup (bin_member);
+ item->spv_info->xml_member = xstrdup_if_nonnull (xml_member);
+ return item;
}
-/* Constructs a new spv_item from XML and stores it in *ITEMP. Returns NULL if
- successful, otherwise an error message for the caller to use and free (with
- free()).
+static cairo_status_t
+read_from_zip_member (void *zm_, unsigned char *data, unsigned int length)
+{
+ struct zip_member *zm = zm_;
+ if (!zm)
+ return CAIRO_STATUS_READ_ERROR;
+
+ while (length > 0)
+ {
+ int n = zip_member_read (zm, data, length);
+ if (n <= 0)
+ return CAIRO_STATUS_READ_ERROR;
+
+ data += n;
+ length -= n;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
- XML should be a 'heading' or 'container' element. */
static char * WARN_UNUSED_RESULT
-spv_decode_container (const struct spvsx_container *c,
- const char *structure_member,
- struct spv_item *parent)
+spv_read_image (struct zip_reader *zip, const char *png_member,
+ const char *command_name, struct output_item **itemp)
+{
+ struct zip_member *zm;
+ char *error = zip_member_open (zip, png_member, &zm);
+ if (error)
+ return error;
+
+ cairo_surface_t *surface = cairo_image_surface_create_from_png_stream (
+ read_from_zip_member, zm);
+ if (zm)
+ zip_member_finish (zm);
+
+ if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS)
+ return xstrdup ("reading image failed");
+
+ struct output_item *item = image_item_create (surface);
+ output_item_set_command_name (item, command_name);
+ output_item_add_spv_info (item);
+ item->spv_info->zip_reader = zip_reader_ref (zip);
+ item->spv_info->png_member = xstrdup (png_member);
+ *itemp = item;
+ return NULL;
+}
+
+static struct output_item *
+error_item_create (char *s)
{
- struct spv_item *item = xzalloc (sizeof *item);
- item->spv = parent->spv;
- item->label = xstrdup (c->label->text);
- item->visible = c->visibility == SPVSX_VISIBILITY_VISIBLE;
- item->structure_member = xstrdup (structure_member);
+ struct output_item *item = text_item_create_nocopy (TEXT_ITEM_LOG, s,
+ xstrdup ("Error"));
+ output_item_add_spv_info (item);
+ item->spv_info->error = true;
+ return item;
+}
+static struct output_item *
+spv_decode_container (struct zip_reader *zip,
+ const struct spvsx_container *c)
+{
assert (c->n_seq == 1);
struct spvxml_node *content = c->seq[0];
+
+ struct output_item *item = NULL;
+ char *error;
if (spvsx_is_container_text (content))
- decode_container_text (spvsx_cast_container_text (content), item);
- else if (spvsx_is_table (content))
- {
- item->type = SPV_ITEM_TABLE;
-
- struct spvsx_table *table = spvsx_cast_table (content);
- const struct spvsx_table_structure *ts = table->table_structure;
- item->bin_member = xstrdup (ts->data_path->text);
- item->command_id = xstrdup_if_nonempty (table->command_name);
- item->subtype = xstrdup_if_nonempty (table->sub_type);
- if (ts->path)
- {
- item->xml_member = ts->path ? xstrdup (ts->path->text) : NULL;
- char *error = (table->table_properties
- ? spv_table_look_decode (table->table_properties,
- &item->table_look)
- : xstrdup ("Legacy table lacks tableProperties"));
- if (error)
- {
- spv_item_destroy (item);
- return error;
- }
- }
- }
- else if (spvsx_is_graph (content))
{
- struct spvsx_graph *graph = spvsx_cast_graph (content);
- item->type = SPV_ITEM_GRAPH;
- item->command_id = xstrdup_if_nonempty (graph->command_name);
- /* XXX */
+ item = decode_container_text (spvsx_cast_container_text (content));
+ error = NULL;
}
- else if (spvsx_is_model (content))
+ else if (spvsx_is_table (content))
{
- struct spvsx_model *model = spvsx_cast_model (content);
- item->type = SPV_ITEM_MODEL;
- item->command_id = xstrdup_if_nonempty (model->command_name);
- /* XXX */
+ item = spv_read_table_item (zip, spvsx_cast_table (content));
+ error = NULL;
}
else if (spvsx_is_object (content))
{
struct spvsx_object *object = spvsx_cast_object (content);
- item->type = SPV_ITEM_IMAGE;
- item->png_member = xstrdup (object->uri);
+ error = spv_read_image (zip, object->uri, object->command_name, &item);
}
else if (spvsx_is_image (content))
{
struct spvsx_image *image = spvsx_cast_image (content);
- item->type = SPV_ITEM_IMAGE;
- item->png_member = xstrdup (image->data_path->text);
+ error = spv_read_image (zip, image->data_path->text, image->command_name,
+ &item);
}
+ else if (spvsx_is_graph (content))
+ error = xstrdup ("graphs not yet implemented");
+ else if (spvsx_is_model (content))
+ error = xstrdup ("models not yet implemented");
else if (spvsx_is_tree (content))
- item->type = SPV_ITEM_TREE;
+ error = xstrdup ("trees not yet implemented");
else
NOT_REACHED ();
- spv_heading_add_child (parent, item);
- return NULL;
+ if (error)
+ item = error_item_create (error);
+ else
+ output_item_set_label (item, c->label->text);
+ item->show = c->visibility == SPVSX_VISIBILITY_VISIBLE;
+
+ return item;
}
-static char * WARN_UNUSED_RESULT
-spv_decode_children (struct spv_reader *spv, const char *structure_member,
+static void
+set_structure_member (struct output_item *item, struct zip_reader *zip,
+ const char *structure_member)
+{
+ if (structure_member)
+ {
+ output_item_add_spv_info (item);
+ if (!item->spv_info->zip_reader)
+ item->spv_info->zip_reader = zip_reader_ref (zip);
+ if (!item->spv_info->structure_member)
+ item->spv_info->structure_member = xstrdup (structure_member);
+ }
+}
+
+static void
+spv_decode_children (struct zip_reader *zip, const char *structure_member,
struct spvxml_node **seq, size_t n_seq,
- struct spv_item *parent)
+ struct output_item *parent)
{
for (size_t i = 0; i < n_seq; i++)
{
const struct spvxml_node *node = seq[i];
- char *error = NULL;
+ struct output_item *child;
if (spvsx_is_container (node))
{
const struct spvsx_container *container
= spvsx_cast_container (node);
- error = spv_decode_container (container, structure_member, parent);
+ child = spv_decode_container (zip, container);
}
else if (spvsx_is_heading (node))
{
const struct spvsx_heading *subheading = spvsx_cast_heading (node);
- struct spv_item *subitem = xzalloc (sizeof *subitem);
- subitem->structure_member = xstrdup (structure_member);
- subitem->spv = parent->spv;
- subitem->type = SPV_ITEM_HEADING;
- subitem->label = xstrdup (subheading->label->text);
- if (subheading->command_name)
- subitem->command_id = xstrdup (subheading->command_name);
- subitem->visible = !subheading->heading_visibility_present;
- spv_heading_add_child (parent, subitem);
-
- error = spv_decode_children (spv, structure_member,
- subheading->seq, subheading->n_seq,
- subitem);
+
+ child = group_item_create (subheading->command_name,
+ subheading->label->text);
+ child->show = !subheading->heading_visibility_present;
+
+ /* Pass NULL for 'structure_member' so that only top-level items get
+ tagged that way. Lower-level items are always in the same
+ structure member as their parent anyway. */
+ spv_decode_children (zip, NULL, subheading->seq,
+ subheading->n_seq, child);
}
else
NOT_REACHED ();
- if (error)
- return error;
+ set_structure_member (child, zip, structure_member);
+ group_item_add_child (parent, child);
}
-
- return NULL;
}
static struct page_setup *
return out;
}
-static char * WARN_UNUSED_RESULT
-spv_heading_read (struct spv_reader *spv,
- const char *file_name, const char *member_name)
+static void
+spv_add_error_heading (struct output_item *root_item,
+ struct zip_reader *zip, const char *structure_member,
+ char *error)
+{
+ struct output_item *item = error_item_create (
+ xasprintf ("%s: %s", structure_member, error));
+ free (error);
+ set_structure_member (item, zip, structure_member);
+ group_item_add_child (root_item, item);
+}
+
+static void
+spv_heading_read (struct zip_reader *zip, struct output_item *root_item,
+ struct page_setup **psp, const char *file_name,
+ const char *structure_member)
{
xmlDoc *doc;
- char *error = spv_read_xml_member (spv, member_name, true, "heading", &doc);
+ char *error = spv_read_xml_member (zip, structure_member, true,
+ "heading", &doc);
if (error)
- return error;
+ {
+ spv_add_error_heading (root_item, zip, structure_member, error);
+ return;
+ }
struct spvxml_context ctx = SPVXML_CONTEXT_INIT (ctx);
struct spvsx_root_heading *root;
spvsx_parse_root_heading (&ctx, xmlDocGetRootElement (doc), &root);
error = spvxml_context_finish (&ctx, &root->node_);
-
- if (!error && root->page_setup)
- spv->page_setup = decode_page_setup (root->page_setup, file_name);
-
- for (size_t i = 0; !error && i < root->n_seq; i++)
- error = spv_decode_children (spv, member_name, root->seq, root->n_seq,
- spv->root);
-
if (error)
{
- char *s = xasprintf ("%s: %s", member_name, error);
- free (error);
- error = s;
+ xmlFreeDoc (doc);
+ spv_add_error_heading (root_item, zip, structure_member, error);
+ return;
}
- spvsx_free_root_heading (root);
- xmlFreeDoc (doc);
+ if (root->page_setup && psp && !*psp)
+ *psp = decode_page_setup (root->page_setup, file_name);
- return error;
-}
+ for (size_t i = 0; i < root->n_seq; i++)
+ spv_decode_children (zip, structure_member, root->seq, root->n_seq,
+ root_item);
-struct spv_item *
-spv_get_root (const struct spv_reader *spv)
-{
- return spv->root;
+ spvsx_free_root_heading (root);
+ xmlFreeDoc (doc);
}
static int
}
char * WARN_UNUSED_RESULT
-spv_open (const char *filename, struct spv_reader **spvp)
+spv_read (const char *filename, struct output_item **outp,
+ struct page_setup **psp)
{
- *spvp = NULL;
+ *outp = NULL;
+ if (psp)
+ *psp = NULL;
struct spv_reader *spv = xzalloc (sizeof *spv);
- char *error = zip_reader_create (filename, &spv->zip);
+ struct zip_reader *zip;
+ char *error = zip_reader_create (filename, &zip);
if (error)
- {
- spv_close (spv);
- return error;
- }
+ return error;
- int detect = spv_detect__ (spv->zip, &error);
+ int detect = spv_detect__ (zip, &error);
if (detect <= 0)
{
- spv_close (spv);
- return error ? error : xasprintf("%s: not an SPV file", filename);
+ zip_reader_unref (zip);
+ return error ? error : xasprintf ("%s: not an SPV file", filename);
}
- spv->root = xzalloc (sizeof *spv->root);
- spv->root->spv = spv;
- spv->root->type = SPV_ITEM_HEADING;
+ *outp = root_item_create ();
for (size_t i = 0; ; i++)
{
- const char *member_name = zip_reader_get_member_name (spv->zip, i);
- if (!member_name)
+ const char *structure_member = zip_reader_get_member_name (zip, i);
+ if (!structure_member)
break;
- struct substring member_name_ss = ss_cstr (member_name);
- if (ss_starts_with (member_name_ss, ss_cstr ("outputViewer"))
- && ss_ends_with (member_name_ss, ss_cstr (".xml")))
- {
- char *error = spv_heading_read (spv, filename, member_name);
- if (error)
- {
- spv_close (spv);
- return error;
- }
- }
+ struct substring structure_member_ss = ss_cstr (structure_member);
+ if (ss_starts_with (structure_member_ss, ss_cstr ("outputViewer"))
+ && ss_ends_with (structure_member_ss, ss_cstr (".xml")))
+ spv_heading_read (zip, *outp, psp, filename, structure_member);
}
- *spvp = spv;
+ zip_reader_unref (zip);
return NULL;
}
-void
-spv_close (struct spv_reader *spv)
-{
- if (spv)
- {
- zip_reader_unref (spv->zip);
- spv_item_destroy (spv->root);
- page_setup_destroy (spv->page_setup);
- free (spv);
- }
-}
-
-void
-spv_item_set_table_look (struct spv_item *item,
- const struct pivot_table_look *look)
-{
- /* If this is a table, install the table look in it.
-
- (We can't just set item->table_look because light tables ignore it and
- legacy tables sometimes override it.) */
- if (spv_item_is_table (item))
- {
- spv_item_load (item);
- pivot_table_set_look (item->table, look);
- }
-
- for (size_t i = 0; i < item->n_children; i++)
- spv_item_set_table_look (item->children[i], look);
-}
-
char * WARN_UNUSED_RESULT
spv_decode_fmt_spec (uint32_t u32, struct fmt_spec *out)
{
#ifndef OUTPUT_SPV_H
#define OUTPUT_SPV_H 1
-/* SPSS Viewer (SPV) file reader.
+/* SPSS Viewer (SPV) file reader. */
- An SPV file, represented as struct spv_reader, contains a number of
- top-level headings, each of which recursively contains other headings and
- tables. Here, we model a heading, text, table, or other element as an
- "item", and a an SPV file as a single root item that contains each of the
- top-level headings as a child item.
- */
-
-#include <cairo.h>
#include <stdbool.h>
-#include <stddef.h>
#include <stdint.h>
+#include <libxml/tree.h>
#include "libpspp/compiler.h"
struct fmt_spec;
-struct pivot_table;
+struct output_item;
+struct page_setup;
struct spv_data;
-struct spv_reader;
struct spvlb_table;
-struct string;
-struct _xmlDoc;
-
-/* SPV files. */
-
-char *spv_open (const char *filename, struct spv_reader **) WARN_UNUSED_RESULT;
-void spv_close (struct spv_reader *);
+struct zip_reader;
+/* Main functions. */
+char *spv_read (const char *filename, struct output_item **,
+ struct page_setup **) WARN_UNUSED_RESULT;
char *spv_detect (const char *filename) WARN_UNUSED_RESULT;
-const char *spv_get_errors (const struct spv_reader *);
-void spv_clear_errors (struct spv_reader *);
-
-struct spv_item *spv_get_root (const struct spv_reader *);
-void spv_item_dump (const struct spv_item *, int indentation);
-
-const struct page_setup *spv_get_page_setup (const struct spv_reader *);
-
-/* Items.
-
- An spv_item represents of the elements that can occur in an SPV file. Items
- form a tree because "heading" items can have an arbitrary number of child
- items, which in turn may also be headings. The root item, that is, the item
- returned by spv_get_root(), is always a heading. */
-
-enum spv_item_type
- {
- SPV_ITEM_HEADING,
- SPV_ITEM_TEXT,
- SPV_ITEM_TABLE,
- SPV_ITEM_GRAPH,
- SPV_ITEM_MODEL,
- SPV_ITEM_IMAGE,
- SPV_ITEM_TREE,
- };
-
-const char *spv_item_type_to_string (enum spv_item_type);
-
-#define SPV_CLASSES \
- SPV_CLASS(CHARTS, "charts") \
- SPV_CLASS(HEADINGS, "headings") \
- SPV_CLASS(LOGS, "logs") \
- SPV_CLASS(MODELS, "models") \
- SPV_CLASS(TABLES, "tables") \
- SPV_CLASS(TEXTS, "texts") \
- SPV_CLASS(TREES, "trees") \
- SPV_CLASS(WARNINGS, "warnings") \
- SPV_CLASS(OUTLINEHEADERS, "outlineheaders") \
- SPV_CLASS(PAGETITLE, "pagetitle") \
- SPV_CLASS(NOTES, "notes") \
- SPV_CLASS(UNKNOWN, "unknown") \
- SPV_CLASS(OTHER, "other")
-enum spv_item_class
- {
-#define SPV_CLASS(ENUM, NAME) SPV_CLASS_##ENUM,
- SPV_CLASSES
-#undef SPV_CLASS
- };
-enum
- {
-#define SPV_CLASS(ENUM, NAME) +1
- SPV_N_CLASSES = SPV_CLASSES
-#undef SPV_CLASS
-};
-#define SPV_ALL_CLASSES ((1u << SPV_N_CLASSES) - 1)
-
-const char *spv_item_class_to_string (enum spv_item_class);
-enum spv_item_class spv_item_class_from_string (const char *);
-
-struct spv_item
- {
- struct spv_reader *spv;
- struct spv_item *parent;
- size_t parent_idx; /* item->parent->children[parent_idx] == item */
-
- bool error;
-
- char *structure_member;
-
- enum spv_item_type type;
- char *label; /* Localized label. */
- char *command_id; /* Non-localized unique command identifier. */
-
- /* Whether the item is visible.
- For SPV_ITEM_HEADING, false indicates that the item is collapsed.
- For SPV_ITEM_TABLE, false indicates that the item is not shown. */
- bool visible;
-
- /* SPV_ITEM_HEADING only. */
- struct spv_item **children;
- size_t n_children, allocated_children;
-
- /* SPV_ITEM_TABLE only. */
- struct pivot_table *table; /* NULL if not yet loaded. */
- struct pivot_table_look *table_look;
- char *bin_member;
- char *xml_member;
- char *subtype;
-
- /* SPV_ITEM_TEXT only. */
- struct pivot_value *text;
-
- /* SPV_ITEM_IMAGE only. */
- char *png_member;
- cairo_surface_t *image;
- };
-
-void spv_item_format_path (const struct spv_item *, struct string *);
-
-void spv_item_load (const struct spv_item *);
-
-enum spv_item_type spv_item_get_type (const struct spv_item *);
-enum spv_item_class spv_item_get_class (const struct spv_item *);
-
-const char *spv_item_get_label (const struct spv_item *);
-
-bool spv_item_is_heading (const struct spv_item *);
-size_t spv_item_get_n_children (const struct spv_item *);
-struct spv_item *spv_item_get_child (const struct spv_item *, size_t idx);
-
-bool spv_item_is_table (const struct spv_item *);
-const struct pivot_table *spv_item_get_table (const struct spv_item *);
-
-bool spv_item_is_text (const struct spv_item *);
-const struct pivot_value *spv_item_get_text (const struct spv_item *);
-
-bool spv_item_is_image (const struct spv_item *);
-cairo_surface_t *spv_item_get_image (const struct spv_item *);
-
-bool spv_item_is_visible (const struct spv_item *);
-
-#define SPV_ITEM_FOR_EACH(ITER, ROOT) \
- for ((ITER) = (ROOT); (ITER) != NULL; (ITER) = spv_item_next(ITER))
-#define SPV_ITEM_FOR_EACH_SKIP_ROOT(ITER, ROOT) \
- for ((ITER) = (ROOT); ((ITER) = spv_item_next(ITER)) != NULL;)
-struct spv_item *spv_item_next (const struct spv_item *);
-
-const struct spv_item *spv_item_get_parent (const struct spv_item *);
-size_t spv_item_get_level (const struct spv_item *);
-
-const char *spv_item_get_command_id (const struct spv_item *);
-const char *spv_item_get_subtype (const struct spv_item *);
-
-char *spv_item_get_structure (const struct spv_item *, struct _xmlDoc **)
- WARN_UNUSED_RESULT;
-
-bool spv_item_is_light_table (const struct spv_item *);
-char *spv_item_get_light_table (const struct spv_item *,
- struct spvlb_table **)
- WARN_UNUSED_RESULT;
-char *spv_item_get_raw_light_table (const struct spv_item *,
- void **data, size_t *size)
- WARN_UNUSED_RESULT;
-
-bool spv_item_is_legacy_table (const struct spv_item *);
-char *spv_item_get_raw_legacy_data (const struct spv_item *item,
- void **data, size_t *size)
- WARN_UNUSED_RESULT;
-char *spv_item_get_legacy_data (const struct spv_item *, struct spv_data *)
- WARN_UNUSED_RESULT;
-char *spv_item_get_legacy_table (const struct spv_item *, struct _xmlDoc **)
- WARN_UNUSED_RESULT;
-
-void spv_item_set_table_look (struct spv_item *,
- const struct pivot_table_look *);
+/* Debugging functions. */
+char *spv_read_light_table (struct zip_reader *, const char *bin_member,
+ struct spvlb_table **) WARN_UNUSED_RESULT;
+char *spv_read_legacy_data (struct zip_reader *, const char *bin_member,
+ struct spv_data *) WARN_UNUSED_RESULT;
+char *spv_read_xml_member (struct zip_reader *, const char *member_name,
+ bool keep_blanks, const char *root_element_name,
+ xmlDoc **) WARN_UNUSED_RESULT;
+/* Helpers. */
char *spv_decode_fmt_spec (uint32_t u32, struct fmt_spec *) WARN_UNUSED_RESULT;
#endif /* output/spv/spv.h */
}
break;
- case OUTPUT_ITEM_GROUP_OPEN:
- break;
-
- case OUTPUT_ITEM_GROUP_CLOSE:
- break;
+ case OUTPUT_ITEM_GROUP:
+ NOT_REACHED ();
case OUTPUT_ITEM_IMAGE:
{
static const struct output_driver_class tex_driver_class =
{
- "tex",
- tex_destroy,
- tex_submit,
- NULL,
+ .name = "tex",
+ .destroy = tex_destroy,
+ .submit = tex_submit,
};
if (view->y > 0)
view->y += view->object_spacing;
- if (item->item->type == OUTPUT_ITEM_GROUP_OPEN)
+ if (item->item->type == OUTPUT_ITEM_GROUP)
continue;
r = xr_fsm_create_for_scrolling (item->item, view->style, cr);
GtkWidget *drawing_area;
int tw, th;
- if (item->type == OUTPUT_ITEM_GROUP_CLOSE)
- {
- if (view->cur_group)
- {
- if (!gtk_tree_path_up (view->cur_group))
- {
- gtk_tree_path_free (view->cur_group);
- view->cur_group = NULL;
- }
- }
- return;
- }
- else if (item->type == OUTPUT_ITEM_TEXT)
+ if (item->type == OUTPUT_ITEM_GROUP)
+ return;
+
+ if (item->type == OUTPUT_ITEM_TEXT)
{
char *text = text_item_get_plain_text (item);
bool text_is_empty = text[0] == '\0';
view_item->drawing_area = NULL;
GdkWindow *win = gtk_widget_get_window (GTK_WIDGET (view->output));
- if (item->type == OUTPUT_ITEM_GROUP_OPEN)
- tw = th = 0;
- else if (win)
+ if (win)
{
view_item->drawing_area = drawing_area = gtk_drawing_area_new ();
else
gtk_tree_store_append (store, &iter, NULL);
- if (item->type == OUTPUT_ITEM_GROUP_OPEN)
- {
- gtk_tree_path_free (view->cur_group);
- view->cur_group = gtk_tree_model_get_path (GTK_TREE_MODEL (store),
- &iter);
- }
-
+ /* XXX group? */
gtk_tree_store_set (store, &iter,
COL_LABEL, output_item_get_label (item),
COL_ADDR, item,
static struct output_driver_class psppire_output_view_driver_class =
{
- "PSPPIRE Output View", /* name */
- NULL, /* destroy */
- psppire_output_view_submit, /* submit */
- NULL, /* flush */
+ .name = "PSPPIRE Output View",
+ .submit = psppire_output_view_submit,
+ .handles_groups = true,
+ .handles_show = true,
};
void
static struct output_driver_class psppire_output_class =
{
- "PSPPIRE", /* name */
- NULL, /* destroy */
- psppire_output_submit, /* submit */
- NULL, /* flush */
+ .name = "PSPPIRE",
+ .submit = psppire_output_submit,
+ .handles_groups = true,
+ .handles_show = true,
};
void
#include "output/output-item.h"
#include "output/pivot-table.h"
#include "output/spv/spv.h"
-#include "output/spv/spv-output.h"
-#include "output/spv/spv-select.h"
#include "helper.h"
#include "psppire-data-window.h"
return dialog;
}
-struct item_path
- {
- const struct spv_item **nodes;
- size_t n;
-
-#define N_STUB 10
- const struct spv_item *stub[N_STUB];
- };
-
-static void
-swap_nodes (const struct spv_item **a, const struct spv_item **b)
-{
- const struct spv_item *tmp = *a;
- *a = *b;
- *b = tmp;
-}
-
-static void
-get_path (const struct spv_item *item, struct item_path *path)
-{
- size_t allocated = 10;
- path->nodes = path->stub;
- path->n = 0;
-
- while (item)
- {
- if (path->n >= allocated)
- {
- if (path->nodes == path->stub)
- path->nodes = xmemdup (path->stub, sizeof path->stub);
- path->nodes = x2nrealloc (path->nodes, &allocated,
- sizeof *path->nodes);
- }
- path->nodes[path->n++] = item;
- item = item->parent;
- }
-
- for (size_t i = 0; i < path->n / 2; i++)
- swap_nodes (&path->nodes[i], &path->nodes[path->n - i - 1]);
-}
-
-static void
-free_path (struct item_path *path)
-{
- if (path && path->nodes != path->stub)
- free (path->nodes);
-}
-
-static void
-dump_heading_transition (const struct spv_item *old,
- const struct spv_item *new)
-{
- if (old == new)
- return;
-
- struct item_path old_path, new_path;
- get_path (old, &old_path);
- get_path (new, &new_path);
-
- size_t common = 0;
- for (; common < old_path.n && common < new_path.n; common++)
- if (old_path.nodes[common] != new_path.nodes[common])
- break;
-
- for (size_t i = common; i < old_path.n; i++)
- output_item_submit (group_close_item_create ());
- for (size_t i = common; i < new_path.n; i++)
- output_item_submit (group_open_item_create (
- new_path.nodes[i]->command_id,
- new_path.nodes[i]->label));
-
- free_path (&old_path);
- free_path (&new_path);
-}
-
void
read_spv_file (const char *filename)
{
- struct spv_reader *spv;
- char *error = spv_open (filename, &spv);
+ struct output_item *root;
+ char *error = spv_read (filename, &root, NULL);
if (error)
{
/* XXX */
return;
}
- struct spv_item **items;
- size_t n_items;
- spv_select (spv, NULL, 0, &items, &n_items);
- struct spv_item *prev_heading = spv_get_root (spv);
- for (size_t i = 0; i < n_items; i++)
- {
- struct spv_item *heading
- = items[i]->type == SPV_ITEM_HEADING ? items[i] : items[i]->parent;
- dump_heading_transition (prev_heading, heading);
- if (items[i]->type == SPV_ITEM_TEXT)
- spv_text_submit (items[i]);
- else if (items[i]->type == SPV_ITEM_TABLE)
- pivot_table_submit (pivot_table_ref (spv_item_get_table (items[i])));
- else if (items[i]->type == SPV_ITEM_IMAGE)
- {
- cairo_surface_t *image = spv_item_get_image (items[i]);
- output_item_submit (image_item_create (cairo_surface_reference (
- image)));
- }
- prev_heading = heading;
- }
- dump_heading_transition (prev_heading, spv_get_root (spv));
- free (items);
- spv_close (spv);
+ output_item_submit_children (root);
}
/* Callback for the file_open action.
AT_SETUP([pspp-output dir])
AT_CHECK([pspp-output dir $srcdir/utilities/regress.spv], [0], [dnl
-- heading "Set" command "Set"
-- heading "Title" command "Title"
+- group "Set" command "Set"
+- group "Title" command "Title"
- text "Page Title" command "Title"
-- heading "Data List" command "Data List"
+- group "Data List" command "Data List"
- table "Reading 1 record from INLINE." command "Data List" subtype "Fixed Data Records"
-- heading "Begin Data" command "Begin Data"
-- heading "List" command "List"
+- group "Begin Data" command "Begin Data"
+- group "List" command "List"
- table "Data List" command "List"
-- heading "Frequencies" command "Frequencies"
+- group "Frequencies" command "Frequencies"
- table "Statistics" command "Frequencies"
- table "v0" command "Frequencies" subtype "Frequencies"
- table "v1" command "Frequencies" subtype "Frequencies"
- table "v2" command "Frequencies" subtype "Frequencies"
-- heading "Regression" command "Regression"
+- group "Regression" command "Regression"
- table "Model Summary (v2)" command "Regression" subtype "Model Summary"
- table "ANOVA (v2)" command "Regression" subtype "ANOVA"
- table "Coefficients (v2)" command "Regression" subtype "Coefficients"
AT_CLEANUP
AT_SETUP([pspp-output --select equal])
-AT_CHECK([pspp-output dir $srcdir/utilities/regress.spv --select=headings],
+AT_CHECK([pspp-output dir $srcdir/utilities/regress.spv --select=tables],
[0], [dnl
-- heading "Set" command "Set"
-- heading "Title" command "Title"
-- heading "Data List" command "Data List"
-- heading "Begin Data" command "Begin Data"
-- heading "List" command "List"
-- heading "Frequencies" command "Frequencies"
-- heading "Regression" command "Regression"
+- table "Reading 1 record from INLINE." command "Data List" subtype "Fixed Data Records"
+- table "Data List" command "List"
+- table "Statistics" command "Frequencies"
+- table "v0" command "Frequencies" subtype "Frequencies"
+- table "v1" command "Frequencies" subtype "Frequencies"
+- table "v2" command "Frequencies" subtype "Frequencies"
+- table "Model Summary (v2)" command "Regression" subtype "Model Summary"
+- table "ANOVA (v2)" command "Regression" subtype "ANOVA"
+- table "Coefficients (v2)" command "Regression" subtype "Coefficients"
])
AT_CLEANUP
AT_SETUP([pspp-output --select unequal])
-AT_CHECK([pspp-output dir $srcdir/utilities/regress.spv --select=^headings],
+AT_CHECK([pspp-output dir $srcdir/utilities/regress.spv --select=^outlineheaders],
[0], [dnl
- - text "Page Title" command "Title"
- - table "Reading 1 record from INLINE." command "Data List" subtype "Fixed Data Records"
- - table "Data List" command "List"
- - table "Statistics" command "Frequencies"
- - table "v0" command "Frequencies" subtype "Frequencies"
- - table "v1" command "Frequencies" subtype "Frequencies"
- - table "v2" command "Frequencies" subtype "Frequencies"
- - table "Model Summary (v2)" command "Regression" subtype "Model Summary"
- - table "ANOVA (v2)" command "Regression" subtype "ANOVA"
- - table "Coefficients (v2)" command "Regression" subtype "Coefficients"
+- text "Page Title" command "Title"
+- table "Reading 1 record from INLINE." command "Data List" subtype "Fixed Data Records"
+- table "Data List" command "List"
+- table "Statistics" command "Frequencies"
+- table "v0" command "Frequencies" subtype "Frequencies"
+- table "v1" command "Frequencies" subtype "Frequencies"
+- table "v2" command "Frequencies" subtype "Frequencies"
+- table "Model Summary (v2)" command "Regression" subtype "Model Summary"
+- table "ANOVA (v2)" command "Regression" subtype "ANOVA"
+- table "Coefficients (v2)" command "Regression" subtype "Coefficients"
])
AT_CLEANUP
AT_SETUP([pspp-output --commands equal])
AT_CHECK([pspp-output dir $srcdir/utilities/regress.spv --commands='reg*'],
[0], [dnl
-- heading "Regression" command "Regression"
+- group "Regression" command "Regression"
- table "Model Summary (v2)" command "Regression" subtype "Model Summary"
- table "ANOVA (v2)" command "Regression" subtype "ANOVA"
- table "Coefficients (v2)" command "Regression" subtype "Coefficients"
AT_SETUP([pspp-output --commands unequal])
AT_CHECK([pspp-output dir $srcdir/utilities/regress.spv --commands='^reg*'],
[0], [dnl
-- heading "Set" command "Set"
-- heading "Title" command "Title"
+- group "Set" command "Set"
+- group "Title" command "Title"
- text "Page Title" command "Title"
-- heading "Data List" command "Data List"
+- group "Data List" command "Data List"
- table "Reading 1 record from INLINE." command "Data List" subtype "Fixed Data Records"
-- heading "Begin Data" command "Begin Data"
-- heading "List" command "List"
+- group "Begin Data" command "Begin Data"
+- group "List" command "List"
- table "Data List" command "List"
-- heading "Frequencies" command "Frequencies"
+- group "Frequencies" command "Frequencies"
- table "Statistics" command "Frequencies"
- table "v0" command "Frequencies" subtype "Frequencies"
- table "v1" command "Frequencies" subtype "Frequencies"
AT_SETUP([pspp-output --nth-commands])
AT_CHECK([pspp-output dir $srcdir/utilities/regress.spv --nth-commands=2,4,6],
[0], [dnl
-- heading "Title" command "Title"
+- group "Title" command "Title"
- text "Page Title" command "Title"
-- heading "Begin Data" command "Begin Data"
-- heading "Frequencies" command "Frequencies"
+- group "Begin Data" command "Begin Data"
+- group "Frequencies" command "Frequencies"
- table "Statistics" command "Frequencies"
- table "v0" command "Frequencies" subtype "Frequencies"
- table "v1" command "Frequencies" subtype "Frequencies"
AT_SETUP([pspp-output --subtypes equal])
AT_CHECK([pspp-output dir $srcdir/utilities/regress.spv --subtypes='freq*'],
[0], [dnl
- - table "v0" command "Frequencies" subtype "Frequencies"
- - table "v1" command "Frequencies" subtype "Frequencies"
- - table "v2" command "Frequencies" subtype "Frequencies"
+- table "v0" command "Frequencies" subtype "Frequencies"
+- table "v1" command "Frequencies" subtype "Frequencies"
+- table "v2" command "Frequencies" subtype "Frequencies"
])
AT_CLEANUP
AT_SETUP([pspp-output --subtypes unequal])
AT_CHECK([pspp-output dir $srcdir/utilities/regress.spv --subtypes='^freq*'],
[0], [dnl
-- heading "Set" command "Set"
-- heading "Title" command "Title"
+- group "Set" command "Set"
+- group "Title" command "Title"
- text "Page Title" command "Title"
-- heading "Data List" command "Data List"
+- group "Data List" command "Data List"
- table "Reading 1 record from INLINE." command "Data List" subtype "Fixed Data Records"
-- heading "Begin Data" command "Begin Data"
-- heading "List" command "List"
+- group "Begin Data" command "Begin Data"
+- group "List" command "List"
- table "Data List" command "List"
-- heading "Frequencies" command "Frequencies"
+- group "Frequencies" command "Frequencies"
- table "Statistics" command "Frequencies"
-- heading "Regression" command "Regression"
+- group "Regression" command "Regression"
- table "Model Summary (v2)" command "Regression" subtype "Model Summary"
- table "ANOVA (v2)" command "Regression" subtype "ANOVA"
- table "Coefficients (v2)" command "Regression" subtype "Coefficients"
AT_SETUP([pspp-output --labels equal])
AT_CHECK([pspp-output dir $srcdir/utilities/regress.spv --labels='v*'],
[0], [dnl
- - table "v0" command "Frequencies" subtype "Frequencies"
- - table "v1" command "Frequencies" subtype "Frequencies"
- - table "v2" command "Frequencies" subtype "Frequencies"
+- table "v0" command "Frequencies" subtype "Frequencies"
+- table "v1" command "Frequencies" subtype "Frequencies"
+- table "v2" command "Frequencies" subtype "Frequencies"
])
AT_CLEANUP
AT_SETUP([pspp-output --labels unequal])
AT_CHECK([pspp-output dir $srcdir/utilities/regress.spv --labels='^data*'],
[0], [dnl
-- heading "Set" command "Set"
-- heading "Title" command "Title"
+- group "Set" command "Set"
+- group "Title" command "Title"
- text "Page Title" command "Title"
- - table "Reading 1 record from INLINE." command "Data List" subtype "Fixed Data Records"
-- heading "Begin Data" command "Begin Data"
-- heading "List" command "List"
-- heading "Frequencies" command "Frequencies"
+- table "Reading 1 record from INLINE." command "Data List" subtype "Fixed Data Records"
+- group "Begin Data" command "Begin Data"
+- group "List" command "List"
+- group "Frequencies" command "Frequencies"
- table "Statistics" command "Frequencies"
- table "v0" command "Frequencies" subtype "Frequencies"
- table "v1" command "Frequencies" subtype "Frequencies"
- table "v2" command "Frequencies" subtype "Frequencies"
-- heading "Regression" command "Regression"
+- group "Regression" command "Regression"
- table "Model Summary (v2)" command "Regression" subtype "Model Summary"
- table "ANOVA (v2)" command "Regression" subtype "ANOVA"
- table "Coefficients (v2)" command "Regression" subtype "Coefficients"
AT_SETUP([pspp-output --instances])
AT_CHECK([pspp-output dir $srcdir/utilities/regress.spv --instances=1],
[0], [dnl
- - text "Page Title" command "Title"
- - table "Reading 1 record from INLINE." command "Data List" subtype "Fixed Data Records"
- - table "Data List" command "List"
- - table "Statistics" command "Frequencies"
- - table "Model Summary (v2)" command "Regression" subtype "Model Summary"
+- text "Page Title" command "Title"
+- table "Reading 1 record from INLINE." command "Data List" subtype "Fixed Data Records"
+- table "Data List" command "List"
+- table "Statistics" command "Frequencies"
+- table "Model Summary (v2)" command "Regression" subtype "Model Summary"
])
AT_CLEANUP
AT_KEYWORDS([--instances last])
AT_CHECK([pspp-output dir $srcdir/utilities/regress.spv --instances=last],
[0], [dnl
- - text "Page Title" command "Title"
- - table "Reading 1 record from INLINE." command "Data List" subtype "Fixed Data Records"
- - table "Data List" command "List"
- - table "v2" command "Frequencies" subtype "Frequencies"
- - table "Coefficients (v2)" command "Regression" subtype "Coefficients"
+- text "Page Title" command "Title"
+- table "Reading 1 record from INLINE." command "Data List" subtype "Fixed Data Records"
+- table "Data List" command "List"
+- table "v2" command "Frequencies" subtype "Frequencies"
+- table "Coefficients (v2)" command "Regression" subtype "Coefficients"
])
AT_CLEANUP
dnl XXX for --show-hidden.
AT_SETUP([pspp-output --or])
-AT_CHECK([pspp-output dir $srcdir/utilities/regress.spv --select=headings --or --labels='v*'],
+AT_CHECK([pspp-output dir $srcdir/utilities/regress.spv --select=outlineheaders --or --labels='v*'],
[0], [dnl
-- heading "Set" command "Set"
-- heading "Title" command "Title"
-- heading "Data List" command "Data List"
-- heading "Begin Data" command "Begin Data"
-- heading "List" command "List"
-- heading "Frequencies" command "Frequencies"
+- group "Set" command "Set"
+- group "Title" command "Title"
+- group "Data List" command "Data List"
+- group "Begin Data" command "Begin Data"
+- group "List" command "List"
+- group "Frequencies" command "Frequencies"
- table "v0" command "Frequencies" subtype "Frequencies"
- table "v1" command "Frequencies" subtype "Frequencies"
- table "v2" command "Frequencies" subtype "Frequencies"
-- heading "Regression" command "Regression"
+- group "Regression" command "Regression"
])
AT_CLEANUP
#include "libpspp/message.h"
#include "libpspp/string-map.h"
#include "libpspp/string-set.h"
+#include "libpspp/zip-reader.h"
#include "output/driver.h"
#include "output/output-item.h"
#include "output/pivot-table.h"
+#include "output/page-setup.h"
+#include "output/select.h"
#include "output/spv/light-binary-parser.h"
#include "output/spv/spv-legacy-data.h"
#include "output/spv/spv-light-decoder.h"
-#include "output/spv/spv-output.h"
-#include "output/spv/spv-select.h"
#include "output/spv/spv-table-look.h"
#include "output/spv/spv.h"
static bool show_member_names;
/* --show-hidden, --select, --commands, ...: Selection criteria. */
-static struct spv_criteria *criteria;
+static struct output_criteria *criteria;
static size_t n_criteria, allocated_criteria;
/* --or: Add new element to 'criteria' array. */
static void developer_usage (void);
static void parse_options (int argc, char **argv);
-static void
-dump_item (const struct spv_item *item)
+static struct output_item *
+annotate_member_names (const struct output_item *in)
{
- if (show_member_names && (item->xml_member || item->bin_member))
- {
- const char *x = item->xml_member;
- const char *b = item->bin_member;
-
- /* The strings below are not marked for translation because they are only
- useful to developers. */
- char *s = (x && b
- ? xasprintf ("%s and %s:", x, b)
- : xasprintf ("%s:", x ? x : b));
- output_item_submit (text_item_create_nocopy (TEXT_ITEM_TITLE, s,
- xstrdup ("Member Names")));
- }
-
- switch (spv_item_get_type (item))
+ if (in->type == OUTPUT_ITEM_GROUP)
{
- case SPV_ITEM_HEADING:
- break;
-
- case SPV_ITEM_TEXT:
- spv_text_submit (item);
- break;
-
- case SPV_ITEM_TABLE:
- pivot_table_submit (pivot_table_ref (spv_item_get_table (item)));
- break;
-
- case SPV_ITEM_GRAPH:
- break;
-
- case SPV_ITEM_MODEL:
- break;
-
- case SPV_ITEM_IMAGE:
- output_item_submit (image_item_create (cairo_surface_reference (
- spv_item_get_image (item))));
- break;
-
- case SPV_ITEM_TREE:
- break;
+ struct output_item *out = group_item_clone_empty (in);
+ for (size_t i = 0; i < in->group.n_children; i++)
+ {
+ const struct output_item *item = in->group.children[i];
+ const char *members[4];
+ size_t n = spv_info_get_members (item->spv_info, members,
+ sizeof members / sizeof *members);
+ if (n)
+ {
+ struct string s = DS_EMPTY_INITIALIZER;
+ ds_put_cstr (&s, members[0]);
+ for (size_t i = 1; i < n; i++)
+ ds_put_format (&s, " and %s", members[i]);
+ group_item_add_child (out, text_item_create_nocopy (
+ TEXT_ITEM_TITLE, ds_steal_cstr (&s),
+ xstrdup ("Member Names")));
+ }
- default:
- abort ();
+ group_item_add_child (out, output_item_ref (item));
+ }
+ return out;
}
+ else
+ return output_item_ref (in);
}
static void
-print_item_directory (const struct spv_item *item)
+print_item_directory (const struct output_item *item, int level)
{
- for (int i = 1; i < spv_item_get_level (item); i++)
+ for (int i = 0; i < level; i++)
printf (" ");
- enum spv_item_type type = spv_item_get_type (item);
- printf ("- %s", spv_item_type_to_string (type));
+ printf ("- %s", output_item_type_to_string (item->type));
- const char *label = spv_item_get_label (item);
+ const char *label = output_item_get_label (item);
if (label)
printf (" \"%s\"", label);
- if (type == SPV_ITEM_TABLE)
+ if (item->type == OUTPUT_ITEM_TABLE)
{
- const struct pivot_table *table = spv_item_get_table (item);
- char *title = pivot_value_to_string (table->title, table);
+ char *title = pivot_value_to_string (item->table->title, item->table);
if (!label || strcmp (title, label))
printf (" title \"%s\"", title);
free (title);
}
- const char *command_id = spv_item_get_command_id (item);
- if (command_id)
- printf (" command \"%s\"", command_id);
+ if (item->command_name)
+ printf (" command \"%s\"", item->command_name);
- const char *subtype = spv_item_get_subtype (item);
- if (subtype && (!label || strcmp (label, subtype)))
- printf (" subtype \"%s\"", subtype);
+ char *subtype = output_item_get_subtype (item);
+ if (subtype)
+ {
+ if (!label || strcmp (label, subtype))
+ printf (" subtype \"%s\"", subtype);
+ free (subtype);
+ }
- if (!spv_item_is_visible (item))
- printf (" (hidden)");
+ if (!item->show)
+ printf (" (%s)", item->type == OUTPUT_ITEM_GROUP ? "collapsed" : "hidden");
if (show_member_names)
{
- const char *members[] = {
- item->xml_member,
- item->bin_member,
- item->png_member,
- };
- size_t n = 0;
+ const char *members[4];
+ size_t n = spv_info_get_members (item->spv_info, members,
+ sizeof members / sizeof *members);
- for (size_t i = 0; i < sizeof members / sizeof *members; i++)
- if (members[i])
- printf (" %s %s", n++ == 0 ? "in" : "and", members[i]);
+ for (size_t i = 0; i < n; i++)
+ printf (" %s %s", i == 0 ? "in" : "and", members[i]);
}
putchar ('\n');
+
+ if (item->type == OUTPUT_ITEM_GROUP)
+ for (size_t i = 0; i < item->group.n_children; i++)
+ print_item_directory (item->group.children[i], level + 1);
}
static void
error (1, 0, "%s", err);
}
-static void
-run_directory (int argc UNUSED, char **argv)
+static struct output_item *
+read_and_filter_spv (const char *name, struct page_setup **psp)
{
- struct spv_reader *spv;
- char *err = spv_open (argv[1], &spv);
+ struct output_item *root;
+ char *err = spv_read (name, &root, psp);
if (err)
error (1, 0, "%s", err);
-
- struct spv_item **items;
- size_t n_items;
- spv_select (spv, criteria, n_criteria, &items, &n_items);
- for (size_t i = 0; i < n_items; i++)
- print_item_directory (items[i]);
- free (items);
-
- spv_close (spv);
-}
-
-struct item_path
- {
- const struct spv_item **nodes;
- size_t n;
-
-#define N_STUB 10
- const struct spv_item *stub[N_STUB];
- };
-
-static void
-swap_nodes (const struct spv_item **a, const struct spv_item **b)
-{
- const struct spv_item *tmp = *a;
- *a = *b;
- *b = tmp;
-}
-
-static void
-get_path (const struct spv_item *item, struct item_path *path)
-{
- size_t allocated = 10;
- path->nodes = path->stub;
- path->n = 0;
-
- while (item)
- {
- if (path->n >= allocated)
- {
- if (path->nodes == path->stub)
- path->nodes = xmemdup (path->stub, sizeof path->stub);
- path->nodes = x2nrealloc (path->nodes, &allocated,
- sizeof *path->nodes);
- }
- path->nodes[path->n++] = item;
- item = item->parent;
- }
-
- for (size_t i = 0; i < path->n / 2; i++)
- swap_nodes (&path->nodes[i], &path->nodes[path->n - i - 1]);
+ return output_select (root, criteria, n_criteria);
}
static void
-free_path (struct item_path *path)
+run_directory (int argc UNUSED, char **argv)
{
- if (path && path->nodes != path->stub)
- free (path->nodes);
+ struct output_item *root = read_and_filter_spv (argv[1], NULL);
+ for (size_t i = 0; i < root->group.n_children; i++)
+ print_item_directory (root->group.children[i], 0);
+ output_item_unref (root);
}
static void
-dump_heading_transition (const struct spv_item *old,
- const struct spv_item *new)
+set_table_look_recursively (struct output_item *item,
+ const struct pivot_table_look *look)
{
- if (old == new)
- return;
-
- struct item_path old_path, new_path;
- get_path (old, &old_path);
- get_path (new, &new_path);
-
- size_t common = 0;
- for (; common < old_path.n && common < new_path.n; common++)
- if (old_path.nodes[common] != new_path.nodes[common])
- break;
-
- for (size_t i = common; i < old_path.n; i++)
- output_item_submit (group_close_item_create ());
- for (size_t i = common; i < new_path.n; i++)
- output_item_submit (group_open_item_create (
- new_path.nodes[i]->command_id,
- new_path.nodes[i]->label));
-
- free_path (&old_path);
- free_path (&new_path);
+ if (item->type == OUTPUT_ITEM_TABLE)
+ pivot_table_set_look (item->table, look);
+ else if (item->type == OUTPUT_ITEM_GROUP)
+ for (size_t i = 0; i < item->group.n_children; i++)
+ set_table_look_recursively (item->group.children[i], look);
}
static void
run_convert (int argc UNUSED, char **argv)
{
- struct spv_reader *spv;
- char *err = spv_open (argv[1], &spv);
- if (err)
- error (1, 0, "%s", err);
-
+ struct page_setup *ps;
+ struct output_item *root = read_and_filter_spv (argv[1], &ps);
if (table_look)
- spv_item_set_table_look (spv_get_root (spv), table_look);
+ set_table_look_recursively (root, table_look);
+ if (show_member_names)
+ {
+ struct output_item *new_root = annotate_member_names (root);
+ output_item_unref (root);
+ root = new_root;
+ }
output_engine_push ();
output_set_filename (argv[1]);
exit (EXIT_FAILURE);
output_driver_register (driver);
- const struct page_setup *ps = spv_get_page_setup (spv);
if (ps)
- output_item_submit (page_setup_item_create (ps));
-
- struct spv_item **items;
- size_t n_items;
- spv_select (spv, criteria, n_criteria, &items, &n_items);
- struct spv_item *prev_heading = spv_get_root (spv);
- for (size_t i = 0; i < n_items; i++)
{
- struct spv_item *heading
- = items[i]->type == SPV_ITEM_HEADING ? items[i] : items[i]->parent;
- dump_heading_transition (prev_heading, heading);
- dump_item (items[i]);
- prev_heading = heading;
+ output_item_submit (page_setup_item_create (ps));
+ page_setup_destroy (ps);
}
- dump_heading_transition (prev_heading, spv_get_root (spv));
- free (items);
-
- spv_close (spv);
+ output_item_submit_children (root);
output_engine_pop ();
fh_done ();
}
static const struct pivot_table *
-get_first_table (const struct spv_reader *spv)
+get_first_table (const struct output_item *item)
{
- struct spv_item **items;
- size_t n_items;
- spv_select (spv, criteria, n_criteria, &items, &n_items);
-
- for (size_t i = 0; i < n_items; i++)
- if (spv_item_is_table (items[i]))
+ if (item->type == OUTPUT_ITEM_TABLE)
+ return item->table;
+ else if (item->type == OUTPUT_ITEM_GROUP)
+ for (size_t i = 0; i < item->group.n_children; i++)
{
- free (items);
- return spv_item_get_table (items[i]);
+ const struct pivot_table *table
+ = get_first_table (item->group.children[i]);
+ if (table)
+ return table;
}
- free (items);
return NULL;
}
struct pivot_table_look *look;
if (strcmp (argv[1], "-"))
{
- struct spv_reader *spv;
- char *err = spv_open (argv[1], &spv);
- if (err)
- error (1, 0, "%s", err);
-
- const struct pivot_table *table = get_first_table (spv);
+ struct output_item *root = read_and_filter_spv (argv[1], NULL);
+ const struct pivot_table *table = get_first_table (root);
if (!table)
error (1, 0, "%s: no tables found", argv[1]);
look = pivot_table_look_ref (pivot_table_get_look (table));
- spv_close (spv);
+ output_item_unref (root);
}
else
look = pivot_table_look_ref (pivot_table_look_builtin_default ());
static void
run_dump (int argc UNUSED, char **argv)
{
- struct spv_reader *spv;
- char *err = spv_open (argv[1], &spv);
- if (err)
- error (1, 0, "%s", err);
-
- struct spv_item **items;
- size_t n_items;
- spv_select (spv, criteria, n_criteria, &items, &n_items);
- for (size_t i = 0; i < n_items; i++)
- if (items[i]->type == SPV_ITEM_TABLE)
- {
- pivot_table_dump (spv_item_get_table (items[i]), 0);
- putchar ('\n');
- }
- free (items);
-
- spv_close (spv);
+ struct output_item *root = read_and_filter_spv (argv[1], NULL);
+ output_item_dump (root, 0);
+ output_item_unref (root);
}
static int
return a < b ? -1 : a > b;
}
+static char * WARN_UNUSED_RESULT
+dump_raw (struct zip_reader *zr, const char *member_name)
+{
+ void *data;
+ size_t size;
+ char *error = zip_member_read_all (zr, member_name, &data, &size);
+ if (!error)
+ {
+ fwrite (data, size, 1, stdout);
+ free (data);
+ }
+ return error;
+}
+
+static void
+dump_light_table (const struct output_item *item)
+{
+ char *error;
+ if (raw)
+ error = dump_raw (item->spv_info->zip_reader,
+ item->spv_info->bin_member);
+ else
+ {
+ struct spvlb_table *table;
+ error = spv_read_light_table (item->spv_info->zip_reader,
+ item->spv_info->bin_member, &table);
+ if (!error)
+ {
+ if (sort)
+ {
+ qsort (table->borders->borders, table->borders->n_borders,
+ sizeof *table->borders->borders, compare_borders);
+ qsort (table->cells->cells, table->cells->n_cells,
+ sizeof *table->cells->cells, compare_cells);
+ }
+ spvlb_print_table (item->spv_info->bin_member, 0, table);
+ spvlb_free_table (table);
+ }
+ }
+ if (error)
+ {
+ msg (ME, "%s", error);
+ free (error);
+ }
+}
+
static void
run_dump_light_table (int argc UNUSED, char **argv)
{
if (raw && isatty (STDOUT_FILENO))
error (1, 0, "not writing binary data to tty");
- struct spv_reader *spv;
- char *err = spv_open (argv[1], &spv);
- if (err)
- error (1, 0, "%s", err);
+ struct output_item *root = read_and_filter_spv (argv[1], NULL);
+ struct output_iterator iter;
+ OUTPUT_ITEM_FOR_EACH (&iter, root)
+ if (iter.cur->type == OUTPUT_ITEM_TABLE && !iter.cur->spv_info->xml_member)
+ dump_light_table (iter.cur);
+ output_item_unref (root);
+}
- struct spv_item **items;
- size_t n_items;
- spv_select (spv, criteria, n_criteria, &items, &n_items);
- for (size_t i = 0; i < n_items; i++)
+static void
+dump_legacy_data (const struct output_item *item)
+{
+ char *error;
+ if (raw)
+ error = dump_raw (item->spv_info->zip_reader,
+ item->spv_info->bin_member);
+ else
{
- if (!spv_item_is_light_table (items[i]))
- continue;
-
- char *error;
- if (raw)
- {
- void *data;
- size_t size;
- error = spv_item_get_raw_light_table (items[i], &data, &size);
- if (!error)
- {
- fwrite (data, size, 1, stdout);
- free (data);
- }
- }
- else
+ struct spv_data data;
+ error = spv_read_legacy_data (item->spv_info->zip_reader,
+ item->spv_info->bin_member, &data);
+ if (!error)
{
- struct spvlb_table *table;
- error = spv_item_get_light_table (items[i], &table);
- if (!error)
- {
- if (sort)
- {
- qsort (table->borders->borders, table->borders->n_borders,
- sizeof *table->borders->borders, compare_borders);
- qsort (table->cells->cells, table->cells->n_cells,
- sizeof *table->cells->cells, compare_cells);
- }
- spvlb_print_table (items[i]->bin_member, 0, table);
- spvlb_free_table (table);
- }
- }
- if (error)
- {
- msg (ME, "%s", error);
- free (error);
+ printf ("%s:\n", item->spv_info->bin_member);
+ spv_data_dump (&data, stdout);
+ spv_data_uninit (&data);
+ printf ("\n");
}
}
- free (items);
-
- spv_close (spv);
+ if (error)
+ {
+ msg (ME, "%s", error);
+ free (error);
+ }
}
static void
run_dump_legacy_data (int argc UNUSED, char **argv)
{
- struct spv_reader *spv;
- char *err = spv_open (argv[1], &spv);
- if (err)
- error (1, 0, "%s", err);
-
if (raw && isatty (STDOUT_FILENO))
error (1, 0, "not writing binary data to tty");
- struct spv_item **items;
- size_t n_items;
- spv_select (spv, criteria, n_criteria, &items, &n_items);
- for (size_t i = 0; i < n_items; i++)
- if (spv_item_is_legacy_table (items[i]))
- {
- struct spv_data data;
- char *error;
- if (raw)
- {
- void *data;
- size_t size;
- error = spv_item_get_raw_legacy_data (items[i], &data, &size);
- if (!error)
- {
- fwrite (data, size, 1, stdout);
- free (data);
- }
- }
- else
- {
- error = spv_item_get_legacy_data (items[i], &data);
- if (!error)
- {
- printf ("%s:\n", items[i]->bin_member);
- spv_data_dump (&data, stdout);
- spv_data_uninit (&data);
- printf ("\n");
- }
- }
-
- if (error)
- {
- msg (ME, "%s", error);
- free (error);
- }
- }
- free (items);
-
- spv_close (spv);
+ struct output_item *root = read_and_filter_spv (argv[1], NULL);
+ struct output_iterator iter;
+ OUTPUT_ITEM_FOR_EACH (&iter, root)
+ if (iter.cur->type == OUTPUT_ITEM_TABLE
+ && iter.cur->spv_info->xml_member
+ && iter.cur->spv_info->bin_member)
+ dump_legacy_data (iter.cur);
+ output_item_unref (root);
}
/* This is really bogus.
}
static void
-run_dump_legacy_table (int argc, char **argv)
+dump_legacy_table (int argc, char **argv, const struct output_item *item)
{
- struct spv_reader *spv;
- char *err = spv_open (argv[1], &spv);
- if (err)
- error (1, 0, "%s", err);
+ xmlDoc *doc;
+ char *error_s = spv_read_xml_member (item->spv_info->zip_reader,
+ item->spv_info->xml_member,
+ false, "visualization", &doc);
+ dump_xml (argc, argv, item->spv_info->xml_member, error_s, doc);
+}
- struct spv_item **items;
- size_t n_items;
- spv_select (spv, criteria, n_criteria, &items, &n_items);
- for (size_t i = 0; i < n_items; i++)
- if (spv_item_is_legacy_table (items[i]))
- {
- xmlDoc *doc;
- char *error_s = spv_item_get_legacy_table (items[i], &doc);
- dump_xml (argc, argv, items[i]->xml_member, error_s, doc);
- }
- free (items);
+static void
+run_dump_legacy_table (int argc, char **argv)
+{
+ struct output_item *root = read_and_filter_spv (argv[1], NULL);
+ struct output_iterator iter;
+ OUTPUT_ITEM_FOR_EACH (&iter, root)
+ if (iter.cur->type == OUTPUT_ITEM_TABLE
+ && iter.cur->spv_info->xml_member)
+ dump_legacy_table (argc, argv, iter.cur);
+ output_item_unref (root);
+}
- spv_close (spv);
+static void
+dump_structure (int argc, char **argv, const struct output_item *item)
+{
+ xmlDoc *doc;
+ char *error_s = spv_read_xml_member (item->spv_info->zip_reader,
+ item->spv_info->structure_member,
+ true, "heading", &doc);
+ dump_xml (argc, argv, item->spv_info->structure_member, error_s, doc);
}
static void
run_dump_structure (int argc, char **argv)
{
- struct spv_reader *spv;
- char *err = spv_open (argv[1], &spv);
- if (err)
- error (1, 0, "%s", err);
+ struct output_item *root = read_and_filter_spv (argv[1], NULL);
- struct spv_item **items;
- size_t n_items;
- spv_select (spv, criteria, n_criteria, &items, &n_items);
const char *last_structure_member = NULL;
- for (size_t i = 0; i < n_items; i++)
- if (!last_structure_member || strcmp (items[i]->structure_member,
- last_structure_member))
- {
- last_structure_member = items[i]->structure_member;
-
- xmlDoc *doc;
- char *error_s = spv_item_get_structure (items[i], &doc);
- dump_xml (argc, argv, items[i]->structure_member, error_s, doc);
- }
- free (items);
+ struct output_iterator iter;
+ OUTPUT_ITEM_FOR_EACH (&iter, root)
+ {
+ const struct output_item *item = iter.cur;
+ if (item->spv_info->structure_member
+ && (!last_structure_member
+ || strcmp (item->spv_info->structure_member,
+ last_structure_member)))
+ {
+ last_structure_member = item->spv_info->structure_member;
+ dump_structure (argc, argv, item);
+ }
+ }
+ output_item_unref (root);
+}
- spv_close (spv);
+static bool
+is_any_legacy (const struct output_item *item)
+{
+ if (item->type == OUTPUT_ITEM_TABLE)
+ return item->spv_info->xml_member != NULL;
+ else if (item->type == OUTPUT_ITEM_GROUP)
+ for (size_t i = 0; i < item->group.n_children; i++)
+ if (is_any_legacy (item->group.children[i]))
+ return true;
+
+ return false;
}
static void
run_is_legacy (int argc UNUSED, char **argv)
{
- struct spv_reader *spv;
- char *err = spv_open (argv[1], &spv);
- if (err)
- error (1, 0, "%s", err);
-
- bool is_legacy = false;
-
- struct spv_item **items;
- size_t n_items;
- spv_select (spv, criteria, n_criteria, &items, &n_items);
- for (size_t i = 0; i < n_items; i++)
- if (spv_item_is_legacy_table (items[i]))
- {
- is_legacy = true;
- break;
- }
- free (items);
-
- spv_close (spv);
+ struct output_item *root = read_and_filter_spv (argv[1], NULL);
+ bool is_legacy = is_any_legacy (root);
+ output_item_unref (root);
exit (is_legacy ? EXIT_SUCCESS : EXIT_FAILURE);
}
}
}
+struct encoded_strings
+ {
+ char *encoding;
+ struct string_array strings;
+ };
+
+struct encoded_strings_table
+ {
+ struct encoded_strings *es;
+ size_t n, allocated;
+ };
+
static void
-run_strings (int argc UNUSED, char **argv)
+collect_strings (const struct output_item *item,
+ struct encoded_strings_table *t)
{
- struct spv_reader *spv;
- char *err = spv_open (argv[1], &spv);
- if (err)
- error (1, 0, "%s", err);
-
- struct encoded_strings
+ char *error;
+ struct spvlb_table *table;
+ error = spv_read_light_table (item->spv_info->zip_reader,
+ item->spv_info->bin_member, &table);
+ if (error)
{
- char *encoding;
- struct string_array strings;
+ msg (ME, "%s", error);
+ free (error);
+ return;
}
- *es = NULL;
- size_t n_es = 0;
- size_t allocated_es = 0;
-
- struct spv_item **items;
- size_t n_items;
- spv_select (spv, criteria, n_criteria, &items, &n_items);
- for (size_t i = 0; i < n_items; i++)
+
+ const char *table_encoding = spvlb_table_get_encoding (table);
+ size_t j = 0;
+ for (j = 0; j < t->n; j++)
+ if (!strcmp (t->es[j].encoding, table_encoding))
+ break;
+ if (j >= t->n)
{
- if (!spv_item_is_light_table (items[i]))
- continue;
+ if (t->n >= t->allocated)
+ t->es = x2nrealloc (t->es, &t->allocated, sizeof *t->es);
+ t->es[t->n++] = (struct encoded_strings) {
+ .encoding = xstrdup (table_encoding),
+ .strings = STRING_ARRAY_INITIALIZER,
+ };
+ }
+ collect_spvlb_strings (table, &t->es[j].strings);
+}
- char *error;
- struct spvlb_table *table;
- error = spv_item_get_light_table (items[i], &table);
- if (error)
- {
- msg (ME, "%s", error);
- free (error);
- continue;
- }
+static void
+run_strings (int argc UNUSED, char **argv)
+{
+ struct output_item *root = read_and_filter_spv (argv[1], NULL);
- const char *table_encoding = spvlb_table_get_encoding (table);
- size_t j = 0;
- for (j = 0; j < n_es; j++)
- if (!strcmp (es[j].encoding, table_encoding))
- break;
- if (j >= n_es)
- {
- if (n_es >= allocated_es)
- es = x2nrealloc (es, &allocated_es, sizeof *es);
- es[n_es++] = (struct encoded_strings) {
- .encoding = xstrdup (table_encoding),
- .strings = STRING_ARRAY_INITIALIZER,
- };
- }
- collect_spvlb_strings (table, &es[j].strings);
+ struct encoded_strings_table t = { .es = NULL };
+ struct output_iterator iter;
+ OUTPUT_ITEM_FOR_EACH (&iter, root)
+ {
+ const struct output_item *item = iter.cur;
+ if (item->type == OUTPUT_ITEM_TABLE
+ && !item->spv_info->xml_member
+ && item->spv_info->bin_member)
+ collect_strings (item, &t);
}
- free (items);
- for (size_t i = 0; i < n_es; i++)
+ for (size_t i = 0; i < t.n; i++)
{
- dump_strings (es[i].encoding, &es[i].strings);
- free (es[i].encoding);
- string_array_destroy (&es[i].strings);
+ dump_strings (t.es[i].encoding, &t.es[i].strings);
+ free (t.es[i].encoding);
+ string_array_destroy (&t.es[i].strings);
}
- free (es);
+ free (t.es);
- spv_close (spv);
+ output_item_unref (root);
}
struct command
return n_warnings ? EXIT_FAILURE : EXIT_SUCCESS;
}
-static struct spv_criteria *
+static struct output_criteria *
get_criteria (void)
{
if (!n_criteria || new_criteria)
if (n_criteria >= allocated_criteria)
criteria = x2nrealloc (criteria, &allocated_criteria,
sizeof *criteria);
- criteria[n_criteria++] = (struct spv_criteria) SPV_CRITERIA_INITIALIZER;
+ criteria[n_criteria++]
+ = (struct output_criteria) OUTPUT_CRITERIA_INITIALIZER;
}
return &criteria[n_criteria - 1];
for (char *token = strtok (arg, ","); token; token = strtok (NULL, ","))
{
if (!strcmp (arg, "all"))
- classes = SPV_ALL_CLASSES;
+ classes = OUTPUT_ALL_CLASSES;
else if (!strcmp (arg, "help"))
{
puts (_("The following object classes are supported:"));
- for (int class = 0; class < SPV_N_CLASSES; class++)
- printf ("- %s\n", spv_item_class_to_string (class));
+ for (int class = 0; class < OUTPUT_N_CLASSES; class++)
+ printf ("- %s\n", output_item_class_to_string (class));
exit (0);
}
else
{
- int class = spv_item_class_from_string (token);
- if (class == SPV_N_CLASSES)
- error (1, 0, _("%s: unknown object class (use --select=help "
+ int class = output_item_class_from_string (token);
+ if (class == OUTPUT_N_CLASSES)
+ error (1, 0, _("unknown object class \"%s\" (use --select=help "
"for help)"), arg);
classes |= 1u << class;
}
}
- struct spv_criteria *c = get_criteria ();
- c->classes = invert ? classes ^ SPV_ALL_CLASSES : classes;
+ struct output_criteria *c = get_criteria ();
+ c->classes = invert ? classes ^ OUTPUT_ALL_CLASSES : classes;
}
-static struct spv_criteria_match *
+static struct output_criteria_match *
get_criteria_match (const char **arg)
{
- struct spv_criteria *c = get_criteria ();
+ struct output_criteria *c = get_criteria ();
if ((*arg)[0] == '^')
{
(*arg)++;
static void
parse_commands (const char *arg)
{
- struct spv_criteria_match *cm = get_criteria_match (&arg);
+ struct output_criteria_match *cm = get_criteria_match (&arg);
string_array_parse (&cm->commands, ss_cstr (arg), ss_cstr (","));
}
static void
parse_subtypes (const char *arg)
{
- struct spv_criteria_match *cm = get_criteria_match (&arg);
+ struct output_criteria_match *cm = get_criteria_match (&arg);
string_array_parse (&cm->subtypes, ss_cstr (arg), ss_cstr (","));
}
static void
parse_labels (const char *arg)
{
- struct spv_criteria_match *cm = get_criteria_match (&arg);
+ struct output_criteria_match *cm = get_criteria_match (&arg);
string_array_parse (&cm->labels, ss_cstr (arg), ss_cstr (","));
}
static void
parse_instances (char *arg)
{
- struct spv_criteria *c = get_criteria ();
+ struct output_criteria *c = get_criteria ();
size_t allocated_instances = c->n_instances;
for (char *token = strtok (arg, ","); token; token = strtok (NULL, ","))
static void
parse_nth_commands (char *arg)
{
- struct spv_criteria *c = get_criteria ();
+ struct output_criteria *c = get_criteria ();
size_t allocated_commands = c->n_commands;
for (char *token = strtok (arg, ","); token; token = strtok (NULL, ","))
static void
parse_members (const char *arg)
{
- struct spv_criteria *cm = get_criteria ();
+ struct output_criteria *cm = get_criteria ();
string_array_parse (&cm->members, ss_cstr (arg), ss_cstr (","));
}
{
pivot_table_look_unref (table_look);
- char *error_s = spv_table_look_read (arg, &table_look);
+ char *error_s = pivot_table_look_read (arg, &table_look);
if (error_s)
error (1, 0, "%s", error_s);
}