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 spv_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 struct spv_table_look *look = spv_table_look_get (table);
359 err = spv_table_look_write (argv[2], look);
361 error (1, 0, "%s", err);
362 spv_table_look_destroy (look);
368 run_dump (int argc UNUSED, char **argv)
370 struct spv_reader *spv;
371 char *err = spv_open (argv[1], &spv);
373 error (1, 0, "%s", err);
375 struct spv_item **items;
377 spv_select (spv, criteria, n_criteria, &items, &n_items);
378 for (size_t i = 0; i < n_items; i++)
379 if (items[i]->type == SPV_ITEM_TABLE)
381 pivot_table_dump (spv_item_get_table (items[i]), 0);
390 compare_borders (const void *a_, const void *b_)
392 const struct spvlb_border *const *ap = a_;
393 const struct spvlb_border *const *bp = b_;
394 uint32_t a = (*ap)->border_type;
395 uint32_t b = (*bp)->border_type;
397 return a < b ? -1 : a > b;
401 compare_cells (const void *a_, const void *b_)
403 const struct spvlb_cell *const *ap = a_;
404 const struct spvlb_cell *const *bp = b_;
405 uint64_t a = (*ap)->index;
406 uint64_t b = (*bp)->index;
408 return a < b ? -1 : a > b;
412 run_dump_light_table (int argc UNUSED, char **argv)
414 if (raw && isatty (STDOUT_FILENO))
415 error (1, 0, "not writing binary data to tty");
417 struct spv_reader *spv;
418 char *err = spv_open (argv[1], &spv);
420 error (1, 0, "%s", err);
422 struct spv_item **items;
424 spv_select (spv, criteria, n_criteria, &items, &n_items);
425 for (size_t i = 0; i < n_items; i++)
427 if (!spv_item_is_light_table (items[i]))
435 error = spv_item_get_raw_light_table (items[i], &data, &size);
438 fwrite (data, size, 1, stdout);
444 struct spvlb_table *table;
445 error = spv_item_get_light_table (items[i], &table);
450 qsort (table->borders->borders, table->borders->n_borders,
451 sizeof *table->borders->borders, compare_borders);
452 qsort (table->cells->cells, table->cells->n_cells,
453 sizeof *table->cells->cells, compare_cells);
455 spvlb_print_table (items[i]->bin_member, 0, table);
456 spvlb_free_table (table);
461 msg (ME, "%s", error);
472 run_dump_legacy_data (int argc UNUSED, char **argv)
474 struct spv_reader *spv;
475 char *err = spv_open (argv[1], &spv);
477 error (1, 0, "%s", err);
479 struct spv_item **items;
481 spv_select (spv, criteria, n_criteria, &items, &n_items);
482 for (size_t i = 0; i < n_items; i++)
483 if (spv_item_is_legacy_table (items[i]))
485 struct spv_data data;
491 error = spv_item_get_raw_legacy_data (items[i], &data, &size);
494 fwrite (data, size, 1, stdout);
500 error = spv_item_get_legacy_data (items[i], &data);
503 printf ("%s:\n", items[i]->bin_member);
504 spv_data_dump (&data, stdout);
505 spv_data_uninit (&data);
512 msg (ME, "%s", error);
521 /* This is really bogus.
523 XPath doesn't have any notion of a default XML namespace, but all of the
524 elements in the documents we're interested in have a namespace. Thus, we'd
525 need to require the XPath expressions to have a namespace on every single
526 element: vis:sourceVariable, vis:graph, and so on. That's a pain. So,
527 instead, we remove the default namespace from everyplace it occurs. XPath
528 does support the null namespace, so this allows sourceVariable, graph,
531 See http://plasmasturm.org/log/259/ and
532 https://mail.gnome.org/archives/xml/2003-April/msg00144.html for more
535 remove_default_xml_namespace (xmlNode *node)
537 if (node->ns && !node->ns->prefix)
540 for (xmlNode *child = node->children; child; child = child->next)
541 remove_default_xml_namespace (child);
545 register_ns (xmlXPathContext *ctx, const char *prefix, const char *uri)
547 xmlXPathRegisterNs (ctx, CHAR_CAST (xmlChar *, prefix),
548 CHAR_CAST (xmlChar *, uri));
551 static xmlXPathContext *
552 create_xpath_context (xmlDoc *doc)
554 xmlXPathContext *ctx = xmlXPathNewContext (doc);
555 register_ns (ctx, "vgr", "http://xml.spss.com/spss/viewer/viewer-graph");
556 register_ns (ctx, "vizml", "http://xml.spss.com/visualization");
557 register_ns (ctx, "vmd", "http://xml.spss.com/spss/viewer/viewer-model");
558 register_ns (ctx, "vps", "http://xml.spss.com/spss/viewer/viewer-pagesetup");
559 register_ns (ctx, "vst", "http://xml.spss.com/spss/viewer/viewer-style");
560 register_ns (ctx, "vtb", "http://xml.spss.com/spss/viewer/viewer-table");
561 register_ns (ctx, "vtl", "http://xml.spss.com/spss/viewer/table-looks");
562 register_ns (ctx, "vtt", "http://xml.spss.com/spss/viewer/viewer-treemodel");
563 register_ns (ctx, "vtx", "http://xml.spss.com/spss/viewer/viewer-text");
564 register_ns (ctx, "xsi", "http://www.w3.org/2001/XMLSchema-instance");
569 dump_xml (int argc, char **argv, const char *member_name,
570 char *error_s, xmlDoc *doc)
576 printf ("<!-- %s -->\n", member_name);
577 xmlElemDump (stdout, NULL, xmlDocGetRootElement (doc));
582 bool any_results = false;
584 remove_default_xml_namespace (xmlDocGetRootElement (doc));
585 for (int i = 2; i < argc; i++)
587 xmlXPathContext *xpath_ctx = create_xpath_context (doc);
588 xmlXPathSetContextNode (xmlDocGetRootElement (doc),
590 xmlXPathObject *xpath_obj = xmlXPathEvalExpression(
591 CHAR_CAST (xmlChar *, argv[i]), xpath_ctx);
593 error (1, 0, _("%s: invalid XPath expression"), argv[i]);
595 const xmlNodeSet *nodes = xpath_obj->nodesetval;
596 if (nodes && nodes->nodeNr > 0)
600 printf ("<!-- %s -->\n", member_name);
603 for (size_t j = 0; j < nodes->nodeNr; j++)
605 xmlElemDump (stdout, doc, nodes->nodeTab[j]);
610 xmlXPathFreeObject (xpath_obj);
611 xmlXPathFreeContext (xpath_ctx);
620 printf ("<!-- %s -->\n", member_name);
621 msg (ME, "%s", error_s);
627 run_dump_legacy_table (int argc, char **argv)
629 struct spv_reader *spv;
630 char *err = spv_open (argv[1], &spv);
632 error (1, 0, "%s", err);
634 struct spv_item **items;
636 spv_select (spv, criteria, n_criteria, &items, &n_items);
637 for (size_t i = 0; i < n_items; i++)
638 if (spv_item_is_legacy_table (items[i]))
641 char *error_s = spv_item_get_legacy_table (items[i], &doc);
642 dump_xml (argc, argv, items[i]->xml_member, error_s, doc);
650 run_dump_structure (int argc, char **argv)
652 struct spv_reader *spv;
653 char *err = spv_open (argv[1], &spv);
655 error (1, 0, "%s", err);
657 struct spv_item **items;
659 spv_select (spv, criteria, n_criteria, &items, &n_items);
660 const char *last_structure_member = NULL;
661 for (size_t i = 0; i < n_items; i++)
662 if (!last_structure_member || strcmp (items[i]->structure_member,
663 last_structure_member))
665 last_structure_member = items[i]->structure_member;
668 char *error_s = spv_item_get_structure (items[i], &doc);
669 dump_xml (argc, argv, items[i]->structure_member, error_s, doc);
677 run_is_legacy (int argc UNUSED, char **argv)
679 struct spv_reader *spv;
680 char *err = spv_open (argv[1], &spv);
682 error (1, 0, "%s", err);
684 bool is_legacy = false;
686 struct spv_item **items;
688 spv_select (spv, criteria, n_criteria, &items, &n_items);
689 for (size_t i = 0; i < n_items; i++)
690 if (spv_item_is_legacy_table (items[i]))
699 exit (is_legacy ? EXIT_SUCCESS : EXIT_FAILURE);
705 int min_args, max_args;
706 void (*run) (int argc, char **argv);
709 static const struct command commands[] =
711 { "detect", 1, 1, run_detect },
712 { "dir", 1, 1, run_directory },
713 { "convert", 2, 2, run_convert },
714 { "get-table-look", 2, 2, run_get_table_look },
716 /* Undocumented commands. */
717 { "dump", 1, 1, run_dump },
718 { "dump-light-table", 1, 1, run_dump_light_table },
719 { "dump-legacy-data", 1, 1, run_dump_legacy_data },
720 { "dump-legacy-table", 1, INT_MAX, run_dump_legacy_table },
721 { "dump-structure", 1, INT_MAX, run_dump_structure },
722 { "is-legacy", 1, 1, run_is_legacy },
724 static const int n_commands = sizeof commands / sizeof *commands;
726 static const struct command *
727 find_command (const char *name)
729 for (size_t i = 0; i < n_commands; i++)
731 const struct command *c = &commands[i];
732 if (!strcmp (name, c->name))
739 emit_msg (const struct msg *m, void *aux UNUSED)
741 if (m->severity == MSG_S_ERROR || m->severity == MSG_S_WARNING)
744 char *s = msg_to_string (m);
745 fprintf (stderr, "%s\n", s);
750 main (int argc, char **argv)
752 set_program_name (argv[0]);
753 msg_set_handler (emit_msg, NULL);
757 parse_options (argc, argv);
763 error (1, 0, _("missing command name (use --help for help)"));
765 const struct command *c = find_command (argv[0]);
767 error (1, 0, _("unknown command \"%s\" (use --help for help)"), argv[0]);
769 int n_args = argc - 1;
770 if (n_args < c->min_args || n_args > c->max_args)
772 if (c->min_args == c->max_args)
775 ngettext ("\"%s\" command takes exactly %d argument",
776 "\"%s\" command takes exactly %d arguments",
777 c->min_args), c->name, c->min_args);
779 else if (c->max_args == INT_MAX)
782 ngettext ("\"%s\" command requires at least %d argument",
783 "\"%s\" command requires at least %d arguments",
784 c->min_args), c->name, c->min_args);
789 _("\"%s\" command requires between %d and %d arguments"),
790 c->name, c->min_args, c->max_args);
796 spv_table_look_destroy (table_look);
799 return n_warnings ? EXIT_FAILURE : EXIT_SUCCESS;
802 static struct spv_criteria *
805 if (!n_criteria || new_criteria)
807 new_criteria = false;
808 if (n_criteria >= allocated_criteria)
809 criteria = x2nrealloc (criteria, &allocated_criteria,
811 criteria[n_criteria++] = (struct spv_criteria) SPV_CRITERIA_INITIALIZER;
814 return &criteria[n_criteria - 1];
818 parse_select (char *arg)
820 bool invert = arg[0] == '^';
823 unsigned classes = 0;
824 for (char *token = strtok (arg, ","); token; token = strtok (NULL, ","))
826 if (!strcmp (arg, "all"))
827 classes = SPV_ALL_CLASSES;
828 else if (!strcmp (arg, "help"))
830 puts (_("The following object classes are supported:"));
831 for (int class = 0; class < SPV_N_CLASSES; class++)
832 printf ("- %s\n", spv_item_class_to_string (class));
837 int class = spv_item_class_from_string (token);
838 if (class == SPV_N_CLASSES)
839 error (1, 0, _("%s: unknown object class (use --select=help "
841 classes |= 1u << class;
845 struct spv_criteria *c = get_criteria ();
846 c->classes = invert ? classes ^ SPV_ALL_CLASSES : classes;
849 static struct spv_criteria_match *
850 get_criteria_match (const char **arg)
852 struct spv_criteria *c = get_criteria ();
853 if ((*arg)[0] == '^')
863 parse_commands (const char *arg)
865 struct spv_criteria_match *cm = get_criteria_match (&arg);
866 string_array_parse (&cm->commands, ss_cstr (arg), ss_cstr (","));
870 parse_subtypes (const char *arg)
872 struct spv_criteria_match *cm = get_criteria_match (&arg);
873 string_array_parse (&cm->subtypes, ss_cstr (arg), ss_cstr (","));
877 parse_labels (const char *arg)
879 struct spv_criteria_match *cm = get_criteria_match (&arg);
880 string_array_parse (&cm->labels, ss_cstr (arg), ss_cstr (","));
884 parse_instances (char *arg)
886 struct spv_criteria *c = get_criteria ();
887 size_t allocated_instances = c->n_instances;
889 for (char *token = strtok (arg, ","); token; token = strtok (NULL, ","))
891 if (c->n_instances >= allocated_instances)
892 c->instances = x2nrealloc (c->instances, &allocated_instances,
893 sizeof *c->instances);
895 c->instances[c->n_instances++] = (!strcmp (token, "last") ? -1
901 parse_nth_commands (char *arg)
903 struct spv_criteria *c = get_criteria ();
904 size_t allocated_commands = c->n_commands;
906 for (char *token = strtok (arg, ","); token; token = strtok (NULL, ","))
908 if (c->n_commands >= allocated_commands)
909 c->commands = x2nrealloc (c->commands, &allocated_commands,
910 sizeof *c->commands);
912 c->commands[c->n_commands++] = atoi (token);
917 parse_members (const char *arg)
919 struct spv_criteria *cm = get_criteria ();
920 string_array_parse (&cm->members, ss_cstr (arg), ss_cstr (","));
924 parse_table_look (const char *arg)
926 spv_table_look_destroy (table_look);
927 char *error_s = spv_table_look_read (arg, &table_look);
929 error (1, 0, "%s", error_s);
933 parse_options (int argc, char *argv[])
939 OPT_MEMBER_NAMES = UCHAR_MAX + 1,
954 static const struct option long_options[] =
956 /* Input selection options. */
957 { "show-hidden", no_argument, NULL, OPT_SHOW_HIDDEN },
958 { "select", required_argument, NULL, OPT_SELECT },
959 { "commands", required_argument, NULL, OPT_COMMANDS },
960 { "nth-commands", required_argument, NULL, OPT_NTH_COMMANDS },
961 { "subtypes", required_argument, NULL, OPT_SUBTYPES },
962 { "labels", required_argument, NULL, OPT_LABELS },
963 { "instances", required_argument, NULL, OPT_INSTANCES },
964 { "members", required_argument, NULL, OPT_MEMBERS },
965 { "errors", no_argument, NULL, OPT_ERRORS },
966 { "or", no_argument, NULL, OPT_OR },
968 /* "dir" command options. */
969 { "member-names", no_argument, NULL, OPT_MEMBER_NAMES },
971 /* "convert" command options. */
972 { "force", no_argument, NULL, 'f' },
973 { "table-look", required_argument, NULL, OPT_TABLE_LOOK },
975 /* "dump-light-table" command options. */
976 { "sort", no_argument, NULL, OPT_SORT },
977 { "raw", no_argument, NULL, OPT_RAW },
979 { "help", no_argument, NULL, 'h' },
980 { "version", no_argument, NULL, 'v' },
982 { NULL, 0, NULL, 0 },
987 c = getopt_long (argc, argv, "O:hvf", long_options, NULL);
994 output_driver_parse_option (optarg, &output_options);
997 case OPT_MEMBER_NAMES:
998 show_member_names = true;
1001 case OPT_SHOW_HIDDEN:
1002 get_criteria ()->include_hidden = true;
1006 parse_select (optarg);
1010 parse_commands (optarg);
1013 case OPT_NTH_COMMANDS:
1014 parse_nth_commands (optarg);
1018 parse_subtypes (optarg);
1022 parse_labels (optarg);
1026 parse_instances (optarg);
1030 parse_members (optarg);
1034 get_criteria ()->error = true;
1038 new_criteria = true;
1049 case OPT_TABLE_LOOK:
1050 parse_table_look (optarg);
1058 version_etc (stdout, "pspp-output", PACKAGE_NAME, PACKAGE_VERSION,
1059 "Ben Pfaff", "John Darrington", NULL_SENTINEL);
1060 exit (EXIT_SUCCESS);
1064 exit (EXIT_SUCCESS);
1067 exit (EXIT_FAILURE);
1075 struct string s = DS_EMPTY_INITIALIZER;
1076 struct string_set formats = STRING_SET_INITIALIZER(formats);
1077 output_get_supported_formats (&formats);
1079 const struct string_set_node *node;
1080 STRING_SET_FOR_EACH (format, node, &formats)
1082 if (!ds_is_empty (&s))
1083 ds_put_byte (&s, ' ');
1084 ds_put_cstr (&s, format);
1086 string_set_destroy (&formats);
1089 %s, a utility for working with SPSS viewer (.spv) files.\n\
1090 Usage: %s [OPTION]... COMMAND ARG...\n\
1092 The following commands are available:\n\
1093 detect FILE Detect whether FILE is an SPV file.\n\
1094 dir FILE List tables and other items in FILE.\n\
1095 convert SOURCE DEST Convert .spv SOURCE to DEST.\n\
1096 get-table-look SOURCE DEST Copies first selected TableLook into DEST\n\
1098 Input selection options for \"dir\" and \"convert\":\n\
1099 --select=CLASS... include only some kinds of objects\n\
1100 --select=help print known object classes\n\
1101 --commands=COMMAND... include only specified COMMANDs\n\
1102 --nth-commands=N... include only the Nth instance of selected commands\n\
1103 --subtypes=SUBTYPE... include only specified SUBTYPEs of output\n\
1104 --labels=LABEL... include only output objects with the given LABELs\n\
1105 --instances=INSTANCE... include only the given object INSTANCEs\n\
1106 --show-hidden include hidden output objects\n\
1107 --or separate two sets of selection options\n\
1109 \"convert\" by default infers the destination's format from its extension.\n\
1110 The known extensions are: %s\n\
1111 The following options override \"convert\" behavior:\n\
1112 -O format=FORMAT set destination format to FORMAT\n\
1113 -O OPTION=VALUE set output option\n\
1114 -f, --force keep output file even given errors\n\
1115 --table-look=FILE override tables' style with TableLook from FILE\n\
1117 --help display this help and exit\n\
1118 --version output version information and exit\n",
1119 program_name, program_name, ds_cstr (&s));