#include <config.h>
+#include <cairo.h>
#include <getopt.h>
#include <limits.h>
#include <stdlib.h>
#include <unistd.h>
+#include <unistr.h>
#include "data/file-handle-def.h"
#include "data/settings.h"
+#include "libpspp/encoding-guesser.h"
#include "libpspp/i18n.h"
#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/group-item.h"
-#include "output/page-setup-item.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-output.h"
-#include "output/spv/spv-select.h"
+#include "output/spv/spv-light-decoder.h"
#include "output/spv/spv-table-look.h"
#include "output/spv/spv.h"
-#include "output/table-item.h"
-#include "output/text-item.h"
#include "gl/c-ctype.h"
#include "gl/error.h"
static struct string_map output_options
= STRING_MAP_INITIALIZER (output_options);
-/* --member-name: Include .zip member name in "dir" output. */
+/* --member-names: Include .zip member name in "dir" output. */
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. */
/* --sort: Sort members under dump-light-table, to make comparisons easier. */
static bool sort;
-/* --raw: Dump raw binary data in dump-light-table. */
+/* --raw: Dump raw binary data in "dump-light-table"; dump all strings in
+ "strings". */
static bool raw;
+/* --no-ascii-only: Drop all-ASCII strings in "strings". */
+static bool exclude_ascii_only;
+
+/* --utf8-only: Only print strings that have UTF-8 multibyte sequences in
+ * "strings". */
+static bool include_utf8_only;
+
/* -f, --force: Keep output file even on error. */
static bool force;
/* --table-look: TableLook to replace table style for conversion. */
-static struct spv_table_look *table_look;
+static struct pivot_table_look *table_look;
/* Number of warnings issued. */
static size_t n_warnings;
static void usage (void);
+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))
+ if (in->type == OUTPUT_ITEM_GROUP)
{
- const char *x = item->xml_member;
- const char *b = item->bin_member;
- char *s = (x && b
- ? xasprintf (_("%s and %s:"), x, b)
- : xasprintf ("%s:", x ? x : b));
- text_item_submit (text_item_create_nocopy (TEXT_ITEM_TITLE, s));
- }
-
- switch (spv_item_get_type (item))
- {
- 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_OBJECT:
- 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,
- SETTINGS_VALUE_SHOW_DEFAULT,
- SETTINGS_VALUE_SHOW_DEFAULT);
+ 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 (!item->show)
+ printf (" (%s)", item->type == OUTPUT_ITEM_GROUP ? "collapsed" : "hidden");
- if (!spv_item_is_visible (item))
- printf (" (hidden)");
- if (show_member_names && (item->xml_member || item->bin_member))
+ if (show_member_names)
{
- if (item->xml_member && item->bin_member)
- printf (" in %s and %s", item->xml_member, item->bin_member);
- else if (item->xml_member)
- printf (" in %s", item->xml_member);
- else if (item->bin_member)
- printf (" in %s", item->bin_member);
+ 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 < 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++)
- group_close_item_submit (group_close_item_create ());
- for (size_t i = common; i < new_path.n; i++)
- group_open_item_submit (group_open_item_create (
- new_path.nodes[i]->command_id));
-
- 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)
- page_setup_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_set_page_setup (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;
}
static void
run_get_table_look (int argc UNUSED, char **argv)
{
- struct spv_reader *spv;
- char *err = spv_open (argv[1], &spv);
- if (err)
- error (1, 0, "%s", err);
+ struct pivot_table_look *look;
+ if (strcmp (argv[1], "-"))
+ {
+ 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]);
- const struct pivot_table *table = get_first_table (spv);
- if (!table)
- error (1, 0, "%s: no tables found", argv[1]);
+ look = pivot_table_look_ref (pivot_table_get_look (table));
- struct spv_table_look *look = spv_table_look_get (table);
- err = spv_table_look_write (argv[2], look);
+ output_item_unref (root);
+ }
+ else
+ look = pivot_table_look_ref (pivot_table_look_builtin_default ());
+
+ char *err = spv_table_look_write (argv[2], look);
if (err)
error (1, 0, "%s", err);
- spv_table_look_destroy (look);
- spv_close (spv);
+ pivot_table_look_unref (look);
}
static void
-run_dump (int argc UNUSED, char **argv)
+run_convert_table_look (int argc UNUSED, char **argv)
{
- struct spv_reader *spv;
- char *err = spv_open (argv[1], &spv);
+ struct pivot_table_look *look;
+ char *err = spv_table_look_read (argv[1], &look);
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);
+ err = spv_table_look_write (argv[2], look);
+ if (err)
+ error (1, 0, "%s", err);
- spv_close (spv);
+ pivot_table_look_unref (look);
+ free (look);
+}
+
+static void
+run_dump (int argc UNUSED, char **argv)
+{
+ 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)
+ struct spv_data data;
+ error = spv_read_legacy_data (item->spv_info->zip_reader,
+ item->spv_info->bin_member, &data);
+ if (!error)
{
- 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 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);
-
- 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);
+ if (raw && isatty (STDOUT_FILENO))
+ error (1, 0, "not writing binary data to tty");
- 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.
xmlXPathFreeContext (xpath_ctx);
}
if (any_results)
- putchar ('\n');;
+ putchar ('\n');
}
xmlFreeDoc (doc);
}
}
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);
+ struct output_item *root = read_and_filter_spv (argv[1], NULL);
+ bool is_legacy = is_any_legacy (root);
+ output_item_unref (root);
- bool is_legacy = false;
+ exit (is_legacy ? EXIT_SUCCESS : EXIT_FAILURE);
+}
- 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);
+static bool
+is_all_ascii (const char *s)
+{
+ for (; *s; s++)
+ if (!encoding_guess_is_ascii_text (*s))
+ return false;
+
+ return true;
+}
- spv_close (spv);
+static void
+dump_strings (const char *encoding, struct string_array *strings)
+{
+ string_array_sort (strings);
+ string_array_uniq (strings);
- exit (is_legacy ? EXIT_SUCCESS : EXIT_FAILURE);
+ if (raw)
+ {
+ if (exclude_ascii_only || include_utf8_only)
+ {
+ size_t i = 0;
+ for (size_t j = 0; j < strings->n; j++)
+ {
+ char *s = strings->strings[j];
+ bool is_ascii = is_all_ascii (s);
+ bool is_utf8 = !u8_check (CHAR_CAST (uint8_t *, s), strlen (s));
+ if (!is_ascii && (!include_utf8_only || is_utf8))
+ strings->strings[i++] = s;
+ else
+ free (s);
+ }
+ strings->n = i;
+ }
+ for (size_t i = 0; i < strings->n; i++)
+ puts (strings->strings[i]);
+ }
+ else
+ {
+ size_t n_nonascii = 0;
+ size_t n_utf8 = 0;
+ for (size_t i = 0; i < strings->n; i++)
+ {
+ const char *s = strings->strings[i];
+ if (!is_all_ascii (s))
+ {
+ n_nonascii++;
+ if (!u8_check (CHAR_CAST (uint8_t *, s), strlen (s)))
+ n_utf8++;
+ }
+ }
+ printf ("%s: %zu unique strings, %zu non-ASCII, %zu UTF-8.\n",
+ encoding, strings->n, n_nonascii, n_utf8);
+ }
+}
+
+struct encoded_strings
+ {
+ char *encoding;
+ struct string_array strings;
+ };
+
+struct encoded_strings_table
+ {
+ struct encoded_strings *es;
+ size_t n, allocated;
+ };
+
+static void
+collect_strings (const struct output_item *item,
+ struct encoded_strings_table *t)
+{
+ char *error;
+ struct spvlb_table *table;
+ error = spv_read_light_table (item->spv_info->zip_reader,
+ item->spv_info->bin_member, &table);
+ if (error)
+ {
+ msg (ME, "%s", error);
+ free (error);
+ return;
+ }
+
+ 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 (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);
+}
+
+static void
+run_strings (int argc UNUSED, char **argv)
+{
+ struct output_item *root = read_and_filter_spv (argv[1], NULL);
+
+ 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);
+ }
+
+ for (size_t i = 0; i < t.n; i++)
+ {
+ dump_strings (t.es[i].encoding, &t.es[i].strings);
+ free (t.es[i].encoding);
+ string_array_destroy (&t.es[i].strings);
+ }
+ free (t.es);
+
+ output_item_unref (root);
}
struct command
{ "dir", 1, 1, run_directory },
{ "convert", 2, 2, run_convert },
{ "get-table-look", 2, 2, run_get_table_look },
+ { "convert-table-look", 2, 2, run_convert_table_look },
/* Undocumented commands. */
{ "dump", 1, 1, run_dump },
{ "dump-legacy-table", 1, INT_MAX, run_dump_legacy_table },
{ "dump-structure", 1, INT_MAX, run_dump_structure },
{ "is-legacy", 1, 1, run_is_legacy },
+ { "strings", 1, 1, run_strings },
};
static const int n_commands = sizeof commands / sizeof *commands;
main (int argc, char **argv)
{
set_program_name (argv[0]);
- msg_set_handler (emit_msg, NULL);
+ msg_set_handler (&(struct msg_handler) { .output_msg = emit_msg });
settings_init ();
i18n_init ();
c->run (argc, argv);
- spv_table_look_destroy (table_look);
+ pivot_table_look_unref (table_look);
i18n_done ();
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 "
- "for help"), arg);
+ 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 (","));
}
static void
parse_table_look (const char *arg)
{
- spv_table_look_destroy (table_look);
- char *error_s = spv_table_look_read (arg, &table_look);
+ pivot_table_look_unref (table_look);
+
+ char *error_s = pivot_table_look_read (arg, &table_look);
if (error_s)
error (1, 0, "%s", error_s);
}
OPT_OR,
OPT_SORT,
OPT_RAW,
+ OPT_NO_ASCII_ONLY,
+ OPT_UTF8_ONLY,
OPT_TABLE_LOOK,
+ OPT_HELP_DEVELOPER,
};
static const struct option long_options[] =
{
{ "sort", no_argument, NULL, OPT_SORT },
{ "raw", no_argument, NULL, OPT_RAW },
+ /* "strings" command options. */
+ { "no-ascii-only", no_argument, NULL, OPT_NO_ASCII_ONLY },
+ { "utf8-only", no_argument, NULL, OPT_UTF8_ONLY },
+
{ "help", no_argument, NULL, 'h' },
+ { "help-developer", no_argument, NULL, OPT_HELP_DEVELOPER },
{ "version", no_argument, NULL, 'v' },
{ NULL, 0, NULL, 0 },
parse_table_look (optarg);
break;
+ case OPT_NO_ASCII_ONLY:
+ exclude_ascii_only = true;
+ break;
+
+ case OPT_UTF8_ONLY:
+ include_utf8_only = true;
+ break;
+
case 'f':
force = true;
break;
usage ();
exit (EXIT_SUCCESS);
+ case OPT_HELP_DEVELOPER:
+ developer_usage ();
+ exit (EXIT_SUCCESS);
+
default:
exit (EXIT_FAILURE);
}
dir FILE List tables and other items in FILE.\n\
convert SOURCE DEST Convert .spv SOURCE to DEST.\n\
get-table-look SOURCE DEST Copies first selected TableLook into DEST\n\
+ convert-table-look SOURCE DEST Copies .tlo or .stt SOURCE into .stt DEST\n\
\n\
Input selection options for \"dir\" and \"convert\":\n\
--select=CLASS... include only some kinds of objects\n\
--table-look=FILE override tables' style with TableLook from FILE\n\
Other options:\n\
--help display this help and exit\n\
+ --help-developer display help for developer commands and exit\n\
--version output version information and exit\n",
program_name, program_name, ds_cstr (&s));
ds_destroy (&s);
}
+
+static void
+developer_usage (void)
+{
+ printf ("\
+The following developer commands are available:\n\
+ dump FILE Dump pivot table structure\n\
+ [--raw | --sort] dump-light-table FILE Dump light tables\n\
+ [--raw] dump-legacy-data FILE Dump legacy table data\n\
+ dump-legacy-table FILE [XPATH]... Dump legacy table XML\n\
+ dump-structure FILE [XPATH]... Dump structure XML\n\
+ is-legacy FILE Exit with status 0 if any legacy table selected\n\
+ strings FILE Dump analysis of strings\n\
+\n\
+Additional input selection options:\n\
+ --members=MEMBER... include only objects with these Zip member names\n\
+ --errors include only objects that cannot be loaded\n\
+\n\
+Additional options for \"dir\" command:\n\
+ --member-names show Zip member names with objects\n\
+\n\
+Options for the \"strings\" command:\n\
+ --raw Dump all (unique) strings\n\
+ --raw --no-ascii-only Dump all strings that contain non-ASCII characters\n\
+ --raw --utf8-only Dump all non-ASCII strings that are valid UTF-8\n\
+\n\
+Other options:\n\
+ --raw print raw binary data instead of a parsed version\n\
+ --sort sort borders and areas for shorter \"diff\" output\n");
+}