1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2017, 2018 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
24 #include "data/file-handle-def.h"
25 #include "data/settings.h"
26 #include "libpspp/i18n.h"
27 #include "libpspp/message.h"
28 #include "libpspp/string-map.h"
29 #include "libpspp/string-set.h"
30 #include "output/driver.h"
31 #include "output/group-item.h"
32 #include "output/page-setup-item.h"
33 #include "output/pivot-table.h"
34 #include "output/spv/light-binary-parser.h"
35 #include "output/spv/spv-legacy-data.h"
36 #include "output/spv/spv-output.h"
37 #include "output/spv/spv-select.h"
38 #include "output/spv/spv-table-look.h"
39 #include "output/spv/spv.h"
40 #include "output/table-item.h"
41 #include "output/text-item.h"
43 #include "gl/c-ctype.h"
45 #include "gl/progname.h"
46 #include "gl/version-etc.h"
47 #include "gl/xalloc.h"
49 #include <libxml/tree.h>
50 #include <libxml/xpath.h>
51 #include <libxml/xpathInternals.h>
54 #define _(msgid) gettext (msgid)
56 /* -O key=value: Output driver options. */
57 static struct string_map output_options
58 = STRING_MAP_INITIALIZER (output_options);
60 /* --member-name: Include .zip member name in "dir" output. */
61 static bool show_member_names;
63 /* --show-hidden, --select, --commands, ...: Selection criteria. */
64 static struct spv_criteria *criteria;
65 static size_t n_criteria, allocated_criteria;
67 /* --or: Add new element to 'criteria' array. */
68 static bool new_criteria;
70 /* --sort: Sort members under dump-light-table, to make comparisons easier. */
73 /* --raw: Dump raw binary data in dump-light-table. */
76 /* -f, --force: Keep output file even on error. */
79 /* --table-look: TableLook to replace table style for conversion. */
80 static struct pivot_table_look *table_look;
82 /* Number of warnings issued. */
83 static size_t n_warnings;
85 static void usage (void);
86 static void parse_options (int argc, char **argv);
89 dump_item (const struct spv_item *item)
91 if (show_member_names && (item->xml_member || item->bin_member))
93 const char *x = item->xml_member;
94 const char *b = item->bin_member;
96 ? xasprintf (_("%s and %s:"), x, b)
97 : xasprintf ("%s:", x ? x : b));
98 text_item_submit (text_item_create_nocopy (TEXT_ITEM_TITLE, s));
101 switch (spv_item_get_type (item))
103 case SPV_ITEM_HEADING:
107 spv_text_submit (item);
111 pivot_table_submit (pivot_table_ref (spv_item_get_table (item)));
120 case SPV_ITEM_OBJECT:
132 print_item_directory (const struct spv_item *item)
134 for (int i = 1; i < spv_item_get_level (item); i++)
137 enum spv_item_type type = spv_item_get_type (item);
138 printf ("- %s", spv_item_type_to_string (type));
140 const char *label = spv_item_get_label (item);
142 printf (" \"%s\"", label);
144 if (type == SPV_ITEM_TABLE)
146 const struct pivot_table *table = spv_item_get_table (item);
147 char *title = pivot_value_to_string (table->title,
148 SETTINGS_VALUE_SHOW_DEFAULT,
149 SETTINGS_VALUE_SHOW_DEFAULT);
150 if (!label || strcmp (title, label))
151 printf (" title \"%s\"", title);
155 const char *command_id = spv_item_get_command_id (item);
157 printf (" command \"%s\"", command_id);
159 const char *subtype = spv_item_get_subtype (item);
160 if (subtype && (!label || strcmp (label, subtype)))
161 printf (" subtype \"%s\"", subtype);
163 if (!spv_item_is_visible (item))
164 printf (" (hidden)");
165 if (show_member_names && (item->xml_member || item->bin_member))
167 if (item->xml_member && item->bin_member)
168 printf (" in %s and %s", item->xml_member, item->bin_member);
169 else if (item->xml_member)
170 printf (" in %s", item->xml_member);
171 else if (item->bin_member)
172 printf (" in %s", item->bin_member);
178 run_detect (int argc UNUSED, char **argv)
180 char *err = spv_detect (argv[1]);
182 error (1, 0, "%s", err);
186 run_directory (int argc UNUSED, char **argv)
188 struct spv_reader *spv;
189 char *err = spv_open (argv[1], &spv);
191 error (1, 0, "%s", err);
193 struct spv_item **items;
195 spv_select (spv, criteria, n_criteria, &items, &n_items);
196 for (size_t i = 0; i < n_items; i++)
197 print_item_directory (items[i]);
205 const struct spv_item **nodes;
209 const struct spv_item *stub[N_STUB];
213 swap_nodes (const struct spv_item **a, const struct spv_item **b)
215 const struct spv_item *tmp = *a;
221 get_path (const struct spv_item *item, struct item_path *path)
223 size_t allocated = 10;
224 path->nodes = path->stub;
229 if (path->n >= allocated)
231 if (path->nodes == path->stub)
232 path->nodes = xmemdup (path->stub, sizeof path->stub);
233 path->nodes = x2nrealloc (path->nodes, &allocated,
234 sizeof *path->nodes);
236 path->nodes[path->n++] = item;
240 for (size_t i = 0; i < path->n / 2; i++)
241 swap_nodes (&path->nodes[i], &path->nodes[path->n - i - 1]);
245 free_path (struct item_path *path)
247 if (path && path->nodes != path->stub)
252 dump_heading_transition (const struct spv_item *old,
253 const struct spv_item *new)
258 struct item_path old_path, new_path;
259 get_path (old, &old_path);
260 get_path (new, &new_path);
263 for (; common < old_path.n && common < new_path.n; common++)
264 if (old_path.nodes[common] != new_path.nodes[common])
267 for (size_t i = common; i < old_path.n; i++)
268 group_close_item_submit (group_close_item_create ());
269 for (size_t i = common; i < new_path.n; i++)
270 group_open_item_submit (group_open_item_create (
271 new_path.nodes[i]->command_id));
273 free_path (&old_path);
274 free_path (&new_path);
278 run_convert (int argc UNUSED, char **argv)
280 struct spv_reader *spv;
281 char *err = spv_open (argv[1], &spv);
283 error (1, 0, "%s", err);
286 spv_item_set_table_look (spv_get_root (spv), table_look);
288 output_engine_push ();
289 output_set_filename (argv[1]);
290 string_map_replace (&output_options, "output-file", argv[2]);
291 struct output_driver *driver = output_driver_create (&output_options);
294 output_driver_register (driver);
296 const struct page_setup *ps = spv_get_page_setup (spv);
298 page_setup_item_submit (page_setup_item_create (ps));
300 struct spv_item **items;
302 spv_select (spv, criteria, n_criteria, &items, &n_items);
303 struct spv_item *prev_heading = spv_get_root (spv);
304 for (size_t i = 0; i < n_items; i++)
306 struct spv_item *heading
307 = items[i]->type == SPV_ITEM_HEADING ? items[i] : items[i]->parent;
308 dump_heading_transition (prev_heading, heading);
309 dump_item (items[i]);
310 prev_heading = heading;
312 dump_heading_transition (prev_heading, spv_get_root (spv));
317 output_engine_pop ();
320 if (n_warnings && !force)
322 /* XXX There could be other files to unlink, e.g. the ascii driver can
323 produce additional files with the charts. */
328 static const struct pivot_table *
329 get_first_table (const struct spv_reader *spv)
331 struct spv_item **items;
333 spv_select (spv, criteria, n_criteria, &items, &n_items);
335 for (size_t i = 0; i < n_items; i++)
336 if (spv_item_is_table (items[i]))
339 return spv_item_get_table (items[i]);
347 run_get_table_look (int argc UNUSED, char **argv)
349 struct spv_reader *spv;
350 char *err = spv_open (argv[1], &spv);
352 error (1, 0, "%s", err);
354 const struct pivot_table *table = get_first_table (spv);
356 error (1, 0, "%s: no tables found", argv[1]);
358 err = spv_table_look_write (argv[2], pivot_table_get_look (table));
360 error (1, 0, "%s", err);
366 run_dump (int argc UNUSED, char **argv)
368 struct spv_reader *spv;
369 char *err = spv_open (argv[1], &spv);
371 error (1, 0, "%s", err);
373 struct spv_item **items;
375 spv_select (spv, criteria, n_criteria, &items, &n_items);
376 for (size_t i = 0; i < n_items; i++)
377 if (items[i]->type == SPV_ITEM_TABLE)
379 pivot_table_dump (spv_item_get_table (items[i]), 0);
388 compare_borders (const void *a_, const void *b_)
390 const struct spvlb_border *const *ap = a_;
391 const struct spvlb_border *const *bp = b_;
392 uint32_t a = (*ap)->border_type;
393 uint32_t b = (*bp)->border_type;
395 return a < b ? -1 : a > b;
399 compare_cells (const void *a_, const void *b_)
401 const struct spvlb_cell *const *ap = a_;
402 const struct spvlb_cell *const *bp = b_;
403 uint64_t a = (*ap)->index;
404 uint64_t b = (*bp)->index;
406 return a < b ? -1 : a > b;
410 run_dump_light_table (int argc UNUSED, char **argv)
412 if (raw && isatty (STDOUT_FILENO))
413 error (1, 0, "not writing binary data to tty");
415 struct spv_reader *spv;
416 char *err = spv_open (argv[1], &spv);
418 error (1, 0, "%s", err);
420 struct spv_item **items;
422 spv_select (spv, criteria, n_criteria, &items, &n_items);
423 for (size_t i = 0; i < n_items; i++)
425 if (!spv_item_is_light_table (items[i]))
433 error = spv_item_get_raw_light_table (items[i], &data, &size);
436 fwrite (data, size, 1, stdout);
442 struct spvlb_table *table;
443 error = spv_item_get_light_table (items[i], &table);
448 qsort (table->borders->borders, table->borders->n_borders,
449 sizeof *table->borders->borders, compare_borders);
450 qsort (table->cells->cells, table->cells->n_cells,
451 sizeof *table->cells->cells, compare_cells);
453 spvlb_print_table (items[i]->bin_member, 0, table);
454 spvlb_free_table (table);
459 msg (ME, "%s", error);
470 run_dump_legacy_data (int argc UNUSED, char **argv)
472 struct spv_reader *spv;
473 char *err = spv_open (argv[1], &spv);
475 error (1, 0, "%s", err);
477 struct spv_item **items;
479 spv_select (spv, criteria, n_criteria, &items, &n_items);
480 for (size_t i = 0; i < n_items; i++)
481 if (spv_item_is_legacy_table (items[i]))
483 struct spv_data data;
489 error = spv_item_get_raw_legacy_data (items[i], &data, &size);
492 fwrite (data, size, 1, stdout);
498 error = spv_item_get_legacy_data (items[i], &data);
501 printf ("%s:\n", items[i]->bin_member);
502 spv_data_dump (&data, stdout);
503 spv_data_uninit (&data);
510 msg (ME, "%s", error);
519 /* This is really bogus.
521 XPath doesn't have any notion of a default XML namespace, but all of the
522 elements in the documents we're interested in have a namespace. Thus, we'd
523 need to require the XPath expressions to have a namespace on every single
524 element: vis:sourceVariable, vis:graph, and so on. That's a pain. So,
525 instead, we remove the default namespace from everyplace it occurs. XPath
526 does support the null namespace, so this allows sourceVariable, graph,
529 See http://plasmasturm.org/log/259/ and
530 https://mail.gnome.org/archives/xml/2003-April/msg00144.html for more
533 remove_default_xml_namespace (xmlNode *node)
535 if (node->ns && !node->ns->prefix)
538 for (xmlNode *child = node->children; child; child = child->next)
539 remove_default_xml_namespace (child);
543 register_ns (xmlXPathContext *ctx, const char *prefix, const char *uri)
545 xmlXPathRegisterNs (ctx, CHAR_CAST (xmlChar *, prefix),
546 CHAR_CAST (xmlChar *, uri));
549 static xmlXPathContext *
550 create_xpath_context (xmlDoc *doc)
552 xmlXPathContext *ctx = xmlXPathNewContext (doc);
553 register_ns (ctx, "vgr", "http://xml.spss.com/spss/viewer/viewer-graph");
554 register_ns (ctx, "vizml", "http://xml.spss.com/visualization");
555 register_ns (ctx, "vmd", "http://xml.spss.com/spss/viewer/viewer-model");
556 register_ns (ctx, "vps", "http://xml.spss.com/spss/viewer/viewer-pagesetup");
557 register_ns (ctx, "vst", "http://xml.spss.com/spss/viewer/viewer-style");
558 register_ns (ctx, "vtb", "http://xml.spss.com/spss/viewer/viewer-table");
559 register_ns (ctx, "vtl", "http://xml.spss.com/spss/viewer/table-looks");
560 register_ns (ctx, "vtt", "http://xml.spss.com/spss/viewer/viewer-treemodel");
561 register_ns (ctx, "vtx", "http://xml.spss.com/spss/viewer/viewer-text");
562 register_ns (ctx, "xsi", "http://www.w3.org/2001/XMLSchema-instance");
567 dump_xml (int argc, char **argv, const char *member_name,
568 char *error_s, xmlDoc *doc)
574 printf ("<!-- %s -->\n", member_name);
575 xmlElemDump (stdout, NULL, xmlDocGetRootElement (doc));
580 bool any_results = false;
582 remove_default_xml_namespace (xmlDocGetRootElement (doc));
583 for (int i = 2; i < argc; i++)
585 xmlXPathContext *xpath_ctx = create_xpath_context (doc);
586 xmlXPathSetContextNode (xmlDocGetRootElement (doc),
588 xmlXPathObject *xpath_obj = xmlXPathEvalExpression(
589 CHAR_CAST (xmlChar *, argv[i]), xpath_ctx);
591 error (1, 0, _("%s: invalid XPath expression"), argv[i]);
593 const xmlNodeSet *nodes = xpath_obj->nodesetval;
594 if (nodes && nodes->nodeNr > 0)
598 printf ("<!-- %s -->\n", member_name);
601 for (size_t j = 0; j < nodes->nodeNr; j++)
603 xmlElemDump (stdout, doc, nodes->nodeTab[j]);
608 xmlXPathFreeObject (xpath_obj);
609 xmlXPathFreeContext (xpath_ctx);
618 printf ("<!-- %s -->\n", member_name);
619 msg (ME, "%s", error_s);
625 run_dump_legacy_table (int argc, char **argv)
627 struct spv_reader *spv;
628 char *err = spv_open (argv[1], &spv);
630 error (1, 0, "%s", err);
632 struct spv_item **items;
634 spv_select (spv, criteria, n_criteria, &items, &n_items);
635 for (size_t i = 0; i < n_items; i++)
636 if (spv_item_is_legacy_table (items[i]))
639 char *error_s = spv_item_get_legacy_table (items[i], &doc);
640 dump_xml (argc, argv, items[i]->xml_member, error_s, doc);
648 run_dump_structure (int argc, char **argv)
650 struct spv_reader *spv;
651 char *err = spv_open (argv[1], &spv);
653 error (1, 0, "%s", err);
655 struct spv_item **items;
657 spv_select (spv, criteria, n_criteria, &items, &n_items);
658 const char *last_structure_member = NULL;
659 for (size_t i = 0; i < n_items; i++)
660 if (!last_structure_member || strcmp (items[i]->structure_member,
661 last_structure_member))
663 last_structure_member = items[i]->structure_member;
666 char *error_s = spv_item_get_structure (items[i], &doc);
667 dump_xml (argc, argv, items[i]->structure_member, error_s, doc);
675 run_is_legacy (int argc UNUSED, char **argv)
677 struct spv_reader *spv;
678 char *err = spv_open (argv[1], &spv);
680 error (1, 0, "%s", err);
682 bool is_legacy = false;
684 struct spv_item **items;
686 spv_select (spv, criteria, n_criteria, &items, &n_items);
687 for (size_t i = 0; i < n_items; i++)
688 if (spv_item_is_legacy_table (items[i]))
697 exit (is_legacy ? EXIT_SUCCESS : EXIT_FAILURE);
703 int min_args, max_args;
704 void (*run) (int argc, char **argv);
707 static const struct command commands[] =
709 { "detect", 1, 1, run_detect },
710 { "dir", 1, 1, run_directory },
711 { "convert", 2, 2, run_convert },
712 { "get-table-look", 2, 2, run_get_table_look },
714 /* Undocumented commands. */
715 { "dump", 1, 1, run_dump },
716 { "dump-light-table", 1, 1, run_dump_light_table },
717 { "dump-legacy-data", 1, 1, run_dump_legacy_data },
718 { "dump-legacy-table", 1, INT_MAX, run_dump_legacy_table },
719 { "dump-structure", 1, INT_MAX, run_dump_structure },
720 { "is-legacy", 1, 1, run_is_legacy },
722 static const int n_commands = sizeof commands / sizeof *commands;
724 static const struct command *
725 find_command (const char *name)
727 for (size_t i = 0; i < n_commands; i++)
729 const struct command *c = &commands[i];
730 if (!strcmp (name, c->name))
737 emit_msg (const struct msg *m, void *aux UNUSED)
739 if (m->severity == MSG_S_ERROR || m->severity == MSG_S_WARNING)
742 char *s = msg_to_string (m);
743 fprintf (stderr, "%s\n", s);
748 main (int argc, char **argv)
750 set_program_name (argv[0]);
751 msg_set_handler (emit_msg, NULL);
755 parse_options (argc, argv);
761 error (1, 0, _("missing command name (use --help for help)"));
763 const struct command *c = find_command (argv[0]);
765 error (1, 0, _("unknown command \"%s\" (use --help for help)"), argv[0]);
767 int n_args = argc - 1;
768 if (n_args < c->min_args || n_args > c->max_args)
770 if (c->min_args == c->max_args)
773 ngettext ("\"%s\" command takes exactly %d argument",
774 "\"%s\" command takes exactly %d arguments",
775 c->min_args), c->name, c->min_args);
777 else if (c->max_args == INT_MAX)
780 ngettext ("\"%s\" command requires at least %d argument",
781 "\"%s\" command requires at least %d arguments",
782 c->min_args), c->name, c->min_args);
787 _("\"%s\" command requires between %d and %d arguments"),
788 c->name, c->min_args, c->max_args);
796 pivot_table_look_uninit (table_look);
801 return n_warnings ? EXIT_FAILURE : EXIT_SUCCESS;
804 static struct spv_criteria *
807 if (!n_criteria || new_criteria)
809 new_criteria = false;
810 if (n_criteria >= allocated_criteria)
811 criteria = x2nrealloc (criteria, &allocated_criteria,
813 criteria[n_criteria++] = (struct spv_criteria) SPV_CRITERIA_INITIALIZER;
816 return &criteria[n_criteria - 1];
820 parse_select (char *arg)
822 bool invert = arg[0] == '^';
825 unsigned classes = 0;
826 for (char *token = strtok (arg, ","); token; token = strtok (NULL, ","))
828 if (!strcmp (arg, "all"))
829 classes = SPV_ALL_CLASSES;
830 else if (!strcmp (arg, "help"))
832 puts (_("The following object classes are supported:"));
833 for (int class = 0; class < SPV_N_CLASSES; class++)
834 printf ("- %s\n", spv_item_class_to_string (class));
839 int class = spv_item_class_from_string (token);
840 if (class == SPV_N_CLASSES)
841 error (1, 0, _("%s: unknown object class (use --select=help "
843 classes |= 1u << class;
847 struct spv_criteria *c = get_criteria ();
848 c->classes = invert ? classes ^ SPV_ALL_CLASSES : classes;
851 static struct spv_criteria_match *
852 get_criteria_match (const char **arg)
854 struct spv_criteria *c = get_criteria ();
855 if ((*arg)[0] == '^')
865 parse_commands (const char *arg)
867 struct spv_criteria_match *cm = get_criteria_match (&arg);
868 string_array_parse (&cm->commands, ss_cstr (arg), ss_cstr (","));
872 parse_subtypes (const char *arg)
874 struct spv_criteria_match *cm = get_criteria_match (&arg);
875 string_array_parse (&cm->subtypes, ss_cstr (arg), ss_cstr (","));
879 parse_labels (const char *arg)
881 struct spv_criteria_match *cm = get_criteria_match (&arg);
882 string_array_parse (&cm->labels, ss_cstr (arg), ss_cstr (","));
886 parse_instances (char *arg)
888 struct spv_criteria *c = get_criteria ();
889 size_t allocated_instances = c->n_instances;
891 for (char *token = strtok (arg, ","); token; token = strtok (NULL, ","))
893 if (c->n_instances >= allocated_instances)
894 c->instances = x2nrealloc (c->instances, &allocated_instances,
895 sizeof *c->instances);
897 c->instances[c->n_instances++] = (!strcmp (token, "last") ? -1
903 parse_nth_commands (char *arg)
905 struct spv_criteria *c = get_criteria ();
906 size_t allocated_commands = c->n_commands;
908 for (char *token = strtok (arg, ","); token; token = strtok (NULL, ","))
910 if (c->n_commands >= allocated_commands)
911 c->commands = x2nrealloc (c->commands, &allocated_commands,
912 sizeof *c->commands);
914 c->commands[c->n_commands++] = atoi (token);
919 parse_members (const char *arg)
921 struct spv_criteria *cm = get_criteria ();
922 string_array_parse (&cm->members, ss_cstr (arg), ss_cstr (","));
926 parse_table_look (const char *arg)
930 pivot_table_look_uninit (table_look);
934 char *error_s = spv_table_look_read (arg, &table_look);
936 error (1, 0, "%s", error_s);
940 parse_options (int argc, char *argv[])
946 OPT_MEMBER_NAMES = UCHAR_MAX + 1,
961 static const struct option long_options[] =
963 /* Input selection options. */
964 { "show-hidden", no_argument, NULL, OPT_SHOW_HIDDEN },
965 { "select", required_argument, NULL, OPT_SELECT },
966 { "commands", required_argument, NULL, OPT_COMMANDS },
967 { "nth-commands", required_argument, NULL, OPT_NTH_COMMANDS },
968 { "subtypes", required_argument, NULL, OPT_SUBTYPES },
969 { "labels", required_argument, NULL, OPT_LABELS },
970 { "instances", required_argument, NULL, OPT_INSTANCES },
971 { "members", required_argument, NULL, OPT_MEMBERS },
972 { "errors", no_argument, NULL, OPT_ERRORS },
973 { "or", no_argument, NULL, OPT_OR },
975 /* "dir" command options. */
976 { "member-names", no_argument, NULL, OPT_MEMBER_NAMES },
978 /* "convert" command options. */
979 { "force", no_argument, NULL, 'f' },
980 { "table-look", required_argument, NULL, OPT_TABLE_LOOK },
982 /* "dump-light-table" command options. */
983 { "sort", no_argument, NULL, OPT_SORT },
984 { "raw", no_argument, NULL, OPT_RAW },
986 { "help", no_argument, NULL, 'h' },
987 { "version", no_argument, NULL, 'v' },
989 { NULL, 0, NULL, 0 },
994 c = getopt_long (argc, argv, "O:hvf", long_options, NULL);
1001 output_driver_parse_option (optarg, &output_options);
1004 case OPT_MEMBER_NAMES:
1005 show_member_names = true;
1008 case OPT_SHOW_HIDDEN:
1009 get_criteria ()->include_hidden = true;
1013 parse_select (optarg);
1017 parse_commands (optarg);
1020 case OPT_NTH_COMMANDS:
1021 parse_nth_commands (optarg);
1025 parse_subtypes (optarg);
1029 parse_labels (optarg);
1033 parse_instances (optarg);
1037 parse_members (optarg);
1041 get_criteria ()->error = true;
1045 new_criteria = true;
1056 case OPT_TABLE_LOOK:
1057 parse_table_look (optarg);
1065 version_etc (stdout, "pspp-output", PACKAGE_NAME, PACKAGE_VERSION,
1066 "Ben Pfaff", "John Darrington", NULL_SENTINEL);
1067 exit (EXIT_SUCCESS);
1071 exit (EXIT_SUCCESS);
1074 exit (EXIT_FAILURE);
1082 struct string s = DS_EMPTY_INITIALIZER;
1083 struct string_set formats = STRING_SET_INITIALIZER(formats);
1084 output_get_supported_formats (&formats);
1086 const struct string_set_node *node;
1087 STRING_SET_FOR_EACH (format, node, &formats)
1089 if (!ds_is_empty (&s))
1090 ds_put_byte (&s, ' ');
1091 ds_put_cstr (&s, format);
1093 string_set_destroy (&formats);
1096 %s, a utility for working with SPSS viewer (.spv) files.\n\
1097 Usage: %s [OPTION]... COMMAND ARG...\n\
1099 The following commands are available:\n\
1100 detect FILE Detect whether FILE is an SPV file.\n\
1101 dir FILE List tables and other items in FILE.\n\
1102 convert SOURCE DEST Convert .spv SOURCE to DEST.\n\
1103 get-table-look SOURCE DEST Copies first selected TableLook into DEST\n\
1105 Input selection options for \"dir\" and \"convert\":\n\
1106 --select=CLASS... include only some kinds of objects\n\
1107 --select=help print known object classes\n\
1108 --commands=COMMAND... include only specified COMMANDs\n\
1109 --nth-commands=N... include only the Nth instance of selected commands\n\
1110 --subtypes=SUBTYPE... include only specified SUBTYPEs of output\n\
1111 --labels=LABEL... include only output objects with the given LABELs\n\
1112 --instances=INSTANCE... include only the given object INSTANCEs\n\
1113 --show-hidden include hidden output objects\n\
1114 --or separate two sets of selection options\n\
1116 \"convert\" by default infers the destination's format from its extension.\n\
1117 The known extensions are: %s\n\
1118 The following options override \"convert\" behavior:\n\
1119 -O format=FORMAT set destination format to FORMAT\n\
1120 -O OPTION=VALUE set output option\n\
1121 -f, --force keep output file even given errors\n\
1122 --table-look=FILE override tables' style with TableLook from FILE\n\
1124 --help display this help and exit\n\
1125 --version output version information and exit\n",
1126 program_name, program_name, ds_cstr (&s));