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/>. */
26 #include "data/file-handle-def.h"
27 #include "data/settings.h"
28 #include "libpspp/encoding-guesser.h"
29 #include "libpspp/i18n.h"
30 #include "libpspp/message.h"
31 #include "libpspp/string-map.h"
32 #include "libpspp/string-set.h"
33 #include "libpspp/zip-reader.h"
34 #include "output/driver.h"
35 #include "output/output-item.h"
36 #include "output/pivot-table.h"
37 #include "output/page-setup.h"
38 #include "output/select.h"
39 #include "output/spv/light-binary-parser.h"
40 #include "output/spv/spv-legacy-data.h"
41 #include "output/spv/spv-light-decoder.h"
42 #include "output/spv/spv-table-look.h"
43 #include "output/spv/spv.h"
45 #include "gl/c-ctype.h"
47 #include "gl/progname.h"
48 #include "gl/version-etc.h"
49 #include "gl/xalloc.h"
51 #include <libxml/tree.h>
52 #include <libxml/xpath.h>
53 #include <libxml/xpathInternals.h>
56 #define _(msgid) gettext (msgid)
58 /* -O key=value: Output driver options. */
59 static struct string_map output_options
60 = STRING_MAP_INITIALIZER (output_options);
62 /* --member-names: Include .zip member name in "dir" output. */
63 static bool show_member_names;
65 /* --show-hidden, --select, --commands, ...: Selection criteria. */
66 static struct output_criteria *criteria;
67 static size_t n_criteria, allocated_criteria;
69 /* --or: Add new element to 'criteria' array. */
70 static bool new_criteria;
72 /* --sort: Sort members under dump-light-table, to make comparisons easier. */
75 /* --raw: Dump raw binary data in "dump-light-table"; dump all strings in
79 /* --no-ascii-only: Drop all-ASCII strings in "strings". */
80 static bool exclude_ascii_only;
82 /* --utf8-only: Only print strings that have UTF-8 multibyte sequences in
84 static bool include_utf8_only;
86 /* -f, --force: Keep output file even on error. */
89 /* --table-look: TableLook to replace table style for conversion. */
90 static struct pivot_table_look *table_look;
92 /* Number of warnings issued. */
93 static size_t n_warnings;
95 static void usage (void);
96 static void developer_usage (void);
97 static void parse_options (int argc, char **argv);
99 static struct output_item *
100 annotate_member_names (const struct output_item *in)
102 if (in->type == OUTPUT_ITEM_GROUP)
104 struct output_item *out = group_item_clone_empty (in);
105 for (size_t i = 0; i < in->group.n_children; i++)
107 const struct output_item *item = in->group.children[i];
108 const char *members[4];
109 size_t n = spv_info_get_members (item->spv_info, members,
110 sizeof members / sizeof *members);
113 struct string s = DS_EMPTY_INITIALIZER;
114 ds_put_cstr (&s, members[0]);
115 for (size_t i = 1; i < n; i++)
116 ds_put_format (&s, " and %s", members[i]);
117 group_item_add_child (out, text_item_create_nocopy (
118 TEXT_ITEM_TITLE, ds_steal_cstr (&s),
119 xstrdup ("Member Names")));
122 group_item_add_child (out, output_item_ref (item));
127 return output_item_ref (in);
131 print_item_directory (const struct output_item *item, int level)
133 for (int i = 0; i < level; i++)
136 printf ("- %s", output_item_type_to_string (item->type));
138 const char *label = output_item_get_label (item);
140 printf (" \"%s\"", label);
142 if (item->type == OUTPUT_ITEM_TABLE)
144 char *title = pivot_value_to_string (item->table->title, item->table);
145 if (!label || strcmp (title, label))
146 printf (" title \"%s\"", title);
150 if (item->command_name)
151 printf (" command \"%s\"", item->command_name);
153 char *subtype = output_item_get_subtype (item);
156 if (!label || strcmp (label, subtype))
157 printf (" subtype \"%s\"", subtype);
162 printf (" (%s)", item->type == OUTPUT_ITEM_GROUP ? "collapsed" : "hidden");
164 if (show_member_names)
166 const char *members[4];
167 size_t n = spv_info_get_members (item->spv_info, members,
168 sizeof members / sizeof *members);
170 for (size_t i = 0; i < n; i++)
171 printf (" %s %s", i == 0 ? "in" : "and", members[i]);
175 if (item->type == OUTPUT_ITEM_GROUP)
176 for (size_t i = 0; i < item->group.n_children; i++)
177 print_item_directory (item->group.children[i], level + 1);
181 run_detect (int argc UNUSED, char **argv)
183 char *err = spv_detect (argv[1]);
185 error (1, 0, "%s", err);
188 static struct output_item *
189 read_and_filter_spv (const char *name, struct page_setup **psp)
191 struct output_item *root;
192 char *err = spv_read (name, &root, psp);
194 error (1, 0, "%s", err);
195 return output_select (root, criteria, n_criteria);
199 run_directory (int argc UNUSED, char **argv)
201 struct output_item *root = read_and_filter_spv (argv[1], NULL);
202 for (size_t i = 0; i < root->group.n_children; i++)
203 print_item_directory (root->group.children[i], 0);
204 output_item_unref (root);
208 set_table_look_recursively (struct output_item *item,
209 const struct pivot_table_look *look)
211 if (item->type == OUTPUT_ITEM_TABLE)
212 pivot_table_set_look (item->table, look);
213 else if (item->type == OUTPUT_ITEM_GROUP)
214 for (size_t i = 0; i < item->group.n_children; i++)
215 set_table_look_recursively (item->group.children[i], look);
219 run_convert (int argc UNUSED, char **argv)
221 struct page_setup *ps;
222 struct output_item *root = read_and_filter_spv (argv[1], &ps);
224 set_table_look_recursively (root, table_look);
225 if (show_member_names)
227 struct output_item *new_root = annotate_member_names (root);
228 output_item_unref (root);
232 output_engine_push ();
233 output_set_filename (argv[1]);
234 string_map_replace (&output_options, "output-file", argv[2]);
235 struct output_driver *driver = output_driver_create (&output_options);
238 output_driver_register (driver);
242 output_set_page_setup (ps);
243 page_setup_destroy (ps);
245 output_item_submit_children (root);
247 output_engine_pop ();
250 if (n_warnings && !force)
252 /* XXX There could be other files to unlink, e.g. the ascii driver can
253 produce additional files with the charts. */
258 static const struct pivot_table *
259 get_first_table (const struct output_item *item)
261 if (item->type == OUTPUT_ITEM_TABLE)
263 else if (item->type == OUTPUT_ITEM_GROUP)
264 for (size_t i = 0; i < item->group.n_children; i++)
266 const struct pivot_table *table
267 = get_first_table (item->group.children[i]);
276 run_get_table_look (int argc UNUSED, char **argv)
278 struct pivot_table_look *look;
279 if (strcmp (argv[1], "-"))
281 struct output_item *root = read_and_filter_spv (argv[1], NULL);
282 const struct pivot_table *table = get_first_table (root);
284 error (1, 0, "%s: no tables found", argv[1]);
286 look = pivot_table_look_ref (pivot_table_get_look (table));
288 output_item_unref (root);
291 look = pivot_table_look_ref (pivot_table_look_builtin_default ());
293 char *err = spv_table_look_write (argv[2], look);
295 error (1, 0, "%s", err);
297 pivot_table_look_unref (look);
301 run_convert_table_look (int argc UNUSED, char **argv)
303 struct pivot_table_look *look;
304 char *err = spv_table_look_read (argv[1], &look);
306 error (1, 0, "%s", err);
308 err = spv_table_look_write (argv[2], look);
310 error (1, 0, "%s", err);
312 pivot_table_look_unref (look);
317 run_dump (int argc UNUSED, char **argv)
319 struct output_item *root = read_and_filter_spv (argv[1], NULL);
320 output_item_dump (root, 0);
321 output_item_unref (root);
325 compare_borders (const void *a_, const void *b_)
327 const struct spvlb_border *const *ap = a_;
328 const struct spvlb_border *const *bp = b_;
329 uint32_t a = (*ap)->border_type;
330 uint32_t b = (*bp)->border_type;
332 return a < b ? -1 : a > b;
336 compare_cells (const void *a_, const void *b_)
338 const struct spvlb_cell *const *ap = a_;
339 const struct spvlb_cell *const *bp = b_;
340 uint64_t a = (*ap)->index;
341 uint64_t b = (*bp)->index;
343 return a < b ? -1 : a > b;
346 static char * WARN_UNUSED_RESULT
347 dump_raw (struct zip_reader *zr, const char *member_name)
351 char *error = zip_member_read_all (zr, member_name, &data, &size);
354 fwrite (data, size, 1, stdout);
361 dump_light_table (const struct output_item *item)
365 error = dump_raw (item->spv_info->zip_reader,
366 item->spv_info->bin_member);
369 struct spvlb_table *table;
370 error = spv_read_light_table (item->spv_info->zip_reader,
371 item->spv_info->bin_member, &table);
376 qsort (table->borders->borders, table->borders->n_borders,
377 sizeof *table->borders->borders, compare_borders);
378 qsort (table->cells->cells, table->cells->n_cells,
379 sizeof *table->cells->cells, compare_cells);
381 spvlb_print_table (item->spv_info->bin_member, 0, table);
382 spvlb_free_table (table);
387 msg (ME, "%s", error);
393 run_dump_light_table (int argc UNUSED, char **argv)
395 if (raw && isatty (STDOUT_FILENO))
396 error (1, 0, "not writing binary data to tty");
398 struct output_item *root = read_and_filter_spv (argv[1], NULL);
399 struct output_iterator iter;
400 OUTPUT_ITEM_FOR_EACH (&iter, root)
401 if (iter.cur->type == OUTPUT_ITEM_TABLE && !iter.cur->spv_info->xml_member)
402 dump_light_table (iter.cur);
403 output_item_unref (root);
407 dump_legacy_data (const struct output_item *item)
411 error = dump_raw (item->spv_info->zip_reader,
412 item->spv_info->bin_member);
415 struct spv_data data;
416 error = spv_read_legacy_data (item->spv_info->zip_reader,
417 item->spv_info->bin_member, &data);
420 printf ("%s:\n", item->spv_info->bin_member);
421 spv_data_dump (&data, stdout);
422 spv_data_uninit (&data);
429 msg (ME, "%s", error);
435 run_dump_legacy_data (int argc UNUSED, char **argv)
437 if (raw && isatty (STDOUT_FILENO))
438 error (1, 0, "not writing binary data to tty");
440 struct output_item *root = read_and_filter_spv (argv[1], NULL);
441 struct output_iterator iter;
442 OUTPUT_ITEM_FOR_EACH (&iter, root)
443 if (iter.cur->type == OUTPUT_ITEM_TABLE
444 && iter.cur->spv_info->xml_member
445 && iter.cur->spv_info->bin_member)
446 dump_legacy_data (iter.cur);
447 output_item_unref (root);
450 /* This is really bogus.
452 XPath doesn't have any notion of a default XML namespace, but all of the
453 elements in the documents we're interested in have a namespace. Thus, we'd
454 need to require the XPath expressions to have a namespace on every single
455 element: vis:sourceVariable, vis:graph, and so on. That's a pain. So,
456 instead, we remove the default namespace from everyplace it occurs. XPath
457 does support the null namespace, so this allows sourceVariable, graph,
460 See http://plasmasturm.org/log/259/ and
461 https://mail.gnome.org/archives/xml/2003-April/msg00144.html for more
464 remove_default_xml_namespace (xmlNode *node)
466 if (node->ns && !node->ns->prefix)
469 for (xmlNode *child = node->children; child; child = child->next)
470 remove_default_xml_namespace (child);
474 register_ns (xmlXPathContext *ctx, const char *prefix, const char *uri)
476 xmlXPathRegisterNs (ctx, CHAR_CAST (xmlChar *, prefix),
477 CHAR_CAST (xmlChar *, uri));
480 static xmlXPathContext *
481 create_xpath_context (xmlDoc *doc)
483 xmlXPathContext *ctx = xmlXPathNewContext (doc);
484 register_ns (ctx, "vgr", "http://xml.spss.com/spss/viewer/viewer-graph");
485 register_ns (ctx, "vizml", "http://xml.spss.com/visualization");
486 register_ns (ctx, "vmd", "http://xml.spss.com/spss/viewer/viewer-model");
487 register_ns (ctx, "vps", "http://xml.spss.com/spss/viewer/viewer-pagesetup");
488 register_ns (ctx, "vst", "http://xml.spss.com/spss/viewer/viewer-style");
489 register_ns (ctx, "vtb", "http://xml.spss.com/spss/viewer/viewer-table");
490 register_ns (ctx, "vtl", "http://xml.spss.com/spss/viewer/table-looks");
491 register_ns (ctx, "vtt", "http://xml.spss.com/spss/viewer/viewer-treemodel");
492 register_ns (ctx, "vtx", "http://xml.spss.com/spss/viewer/viewer-text");
493 register_ns (ctx, "xsi", "http://www.w3.org/2001/XMLSchema-instance");
498 dump_xml (int argc, char **argv, const char *member_name,
499 char *error_s, xmlDoc *doc)
505 printf ("<!-- %s -->\n", member_name);
506 xmlElemDump (stdout, NULL, xmlDocGetRootElement (doc));
511 bool any_results = false;
513 remove_default_xml_namespace (xmlDocGetRootElement (doc));
514 for (int i = 2; i < argc; i++)
516 xmlXPathContext *xpath_ctx = create_xpath_context (doc);
517 xmlXPathSetContextNode (xmlDocGetRootElement (doc),
519 xmlXPathObject *xpath_obj = xmlXPathEvalExpression(
520 CHAR_CAST (xmlChar *, argv[i]), xpath_ctx);
522 error (1, 0, _("%s: invalid XPath expression"), argv[i]);
524 const xmlNodeSet *nodes = xpath_obj->nodesetval;
525 if (nodes && nodes->nodeNr > 0)
529 printf ("<!-- %s -->\n", member_name);
532 for (size_t j = 0; j < nodes->nodeNr; j++)
534 xmlElemDump (stdout, doc, nodes->nodeTab[j]);
539 xmlXPathFreeObject (xpath_obj);
540 xmlXPathFreeContext (xpath_ctx);
549 printf ("<!-- %s -->\n", member_name);
550 msg (ME, "%s", error_s);
556 dump_legacy_table (int argc, char **argv, const struct output_item *item)
559 char *error_s = spv_read_xml_member (item->spv_info->zip_reader,
560 item->spv_info->xml_member,
561 false, "visualization", &doc);
562 dump_xml (argc, argv, item->spv_info->xml_member, error_s, doc);
566 run_dump_legacy_table (int argc, char **argv)
568 struct output_item *root = read_and_filter_spv (argv[1], NULL);
569 struct output_iterator iter;
570 OUTPUT_ITEM_FOR_EACH (&iter, root)
571 if (iter.cur->type == OUTPUT_ITEM_TABLE
572 && iter.cur->spv_info->xml_member)
573 dump_legacy_table (argc, argv, iter.cur);
574 output_item_unref (root);
578 dump_structure (int argc, char **argv, const struct output_item *item)
581 char *error_s = spv_read_xml_member (item->spv_info->zip_reader,
582 item->spv_info->structure_member,
583 true, "heading", &doc);
584 dump_xml (argc, argv, item->spv_info->structure_member, error_s, doc);
588 run_dump_structure (int argc, char **argv)
590 struct output_item *root = read_and_filter_spv (argv[1], NULL);
592 const char *last_structure_member = NULL;
593 struct output_iterator iter;
594 OUTPUT_ITEM_FOR_EACH (&iter, root)
596 const struct output_item *item = iter.cur;
597 if (item->spv_info->structure_member
598 && (!last_structure_member
599 || strcmp (item->spv_info->structure_member,
600 last_structure_member)))
602 last_structure_member = item->spv_info->structure_member;
603 dump_structure (argc, argv, item);
606 output_item_unref (root);
610 is_any_legacy (const struct output_item *item)
612 if (item->type == OUTPUT_ITEM_TABLE)
613 return item->spv_info->xml_member != NULL;
614 else if (item->type == OUTPUT_ITEM_GROUP)
615 for (size_t i = 0; i < item->group.n_children; i++)
616 if (is_any_legacy (item->group.children[i]))
623 run_is_legacy (int argc UNUSED, char **argv)
625 struct output_item *root = read_and_filter_spv (argv[1], NULL);
626 bool is_legacy = is_any_legacy (root);
627 output_item_unref (root);
629 exit (is_legacy ? EXIT_SUCCESS : EXIT_FAILURE);
633 is_all_ascii (const char *s)
636 if (!encoding_guess_is_ascii_text (*s))
643 dump_strings (const char *encoding, struct string_array *strings)
645 string_array_sort (strings);
646 string_array_uniq (strings);
650 if (exclude_ascii_only || include_utf8_only)
653 for (size_t j = 0; j < strings->n; j++)
655 char *s = strings->strings[j];
656 bool is_ascii = is_all_ascii (s);
657 bool is_utf8 = !u8_check (CHAR_CAST (uint8_t *, s), strlen (s));
658 if (!is_ascii && (!include_utf8_only || is_utf8))
659 strings->strings[i++] = s;
665 for (size_t i = 0; i < strings->n; i++)
666 puts (strings->strings[i]);
670 size_t n_nonascii = 0;
672 for (size_t i = 0; i < strings->n; i++)
674 const char *s = strings->strings[i];
675 if (!is_all_ascii (s))
678 if (!u8_check (CHAR_CAST (uint8_t *, s), strlen (s)))
682 printf ("%s: %zu unique strings, %zu non-ASCII, %zu UTF-8.\n",
683 encoding, strings->n, n_nonascii, n_utf8);
687 struct encoded_strings
690 struct string_array strings;
693 struct encoded_strings_table
695 struct encoded_strings *es;
700 collect_strings (const struct output_item *item,
701 struct encoded_strings_table *t)
704 struct spvlb_table *table;
705 error = spv_read_light_table (item->spv_info->zip_reader,
706 item->spv_info->bin_member, &table);
709 msg (ME, "%s", error);
714 const char *table_encoding = spvlb_table_get_encoding (table);
716 for (j = 0; j < t->n; j++)
717 if (!strcmp (t->es[j].encoding, table_encoding))
721 if (t->n >= t->allocated)
722 t->es = x2nrealloc (t->es, &t->allocated, sizeof *t->es);
723 t->es[t->n++] = (struct encoded_strings) {
724 .encoding = xstrdup (table_encoding),
725 .strings = STRING_ARRAY_INITIALIZER,
728 collect_spvlb_strings (table, &t->es[j].strings);
732 run_strings (int argc UNUSED, char **argv)
734 struct output_item *root = read_and_filter_spv (argv[1], NULL);
736 struct encoded_strings_table t = { .es = NULL };
737 struct output_iterator iter;
738 OUTPUT_ITEM_FOR_EACH (&iter, root)
740 const struct output_item *item = iter.cur;
741 if (item->type == OUTPUT_ITEM_TABLE
742 && !item->spv_info->xml_member
743 && item->spv_info->bin_member)
744 collect_strings (item, &t);
747 for (size_t i = 0; i < t.n; i++)
749 dump_strings (t.es[i].encoding, &t.es[i].strings);
750 free (t.es[i].encoding);
751 string_array_destroy (&t.es[i].strings);
755 output_item_unref (root);
761 int min_args, max_args;
762 void (*run) (int argc, char **argv);
765 static const struct command commands[] =
767 { "detect", 1, 1, run_detect },
768 { "dir", 1, 1, run_directory },
769 { "convert", 2, 2, run_convert },
770 { "get-table-look", 2, 2, run_get_table_look },
771 { "convert-table-look", 2, 2, run_convert_table_look },
773 /* Undocumented commands. */
774 { "dump", 1, 1, run_dump },
775 { "dump-light-table", 1, 1, run_dump_light_table },
776 { "dump-legacy-data", 1, 1, run_dump_legacy_data },
777 { "dump-legacy-table", 1, INT_MAX, run_dump_legacy_table },
778 { "dump-structure", 1, INT_MAX, run_dump_structure },
779 { "is-legacy", 1, 1, run_is_legacy },
780 { "strings", 1, 1, run_strings },
782 static const int n_commands = sizeof commands / sizeof *commands;
784 static const struct command *
785 find_command (const char *name)
787 for (size_t i = 0; i < n_commands; i++)
789 const struct command *c = &commands[i];
790 if (!strcmp (name, c->name))
797 emit_msg (const struct msg *m, void *aux UNUSED)
799 if (m->severity == MSG_S_ERROR || m->severity == MSG_S_WARNING)
802 char *s = msg_to_string (m);
803 fprintf (stderr, "%s\n", s);
808 main (int argc, char **argv)
810 set_program_name (argv[0]);
811 msg_set_handler (&(struct msg_handler) { .output_msg = emit_msg });
815 parse_options (argc, argv);
821 error (1, 0, _("missing command name (use --help for help)"));
823 const struct command *c = find_command (argv[0]);
825 error (1, 0, _("unknown command \"%s\" (use --help for help)"), argv[0]);
827 int n_args = argc - 1;
828 if (n_args < c->min_args || n_args > c->max_args)
830 if (c->min_args == c->max_args)
833 ngettext ("\"%s\" command takes exactly %d argument",
834 "\"%s\" command takes exactly %d arguments",
835 c->min_args), c->name, c->min_args);
837 else if (c->max_args == INT_MAX)
840 ngettext ("\"%s\" command requires at least %d argument",
841 "\"%s\" command requires at least %d arguments",
842 c->min_args), c->name, c->min_args);
847 _("\"%s\" command requires between %d and %d arguments"),
848 c->name, c->min_args, c->max_args);
854 pivot_table_look_unref (table_look);
857 return n_warnings ? EXIT_FAILURE : EXIT_SUCCESS;
860 static struct output_criteria *
863 if (!n_criteria || new_criteria)
865 new_criteria = false;
866 if (n_criteria >= allocated_criteria)
867 criteria = x2nrealloc (criteria, &allocated_criteria,
869 criteria[n_criteria++]
870 = (struct output_criteria) OUTPUT_CRITERIA_INITIALIZER;
873 return &criteria[n_criteria - 1];
877 parse_select (char *arg)
879 bool invert = arg[0] == '^';
882 unsigned classes = 0;
883 for (char *token = strtok (arg, ","); token; token = strtok (NULL, ","))
885 if (!strcmp (arg, "all"))
886 classes = OUTPUT_ALL_CLASSES;
887 else if (!strcmp (arg, "help"))
889 puts (_("The following object classes are supported:"));
890 for (int class = 0; class < OUTPUT_N_CLASSES; class++)
891 printf ("- %s\n", output_item_class_to_string (class));
896 int class = output_item_class_from_string (token);
897 if (class == OUTPUT_N_CLASSES)
898 error (1, 0, _("unknown object class \"%s\" (use --select=help "
900 classes |= 1u << class;
904 struct output_criteria *c = get_criteria ();
905 c->classes = invert ? classes ^ OUTPUT_ALL_CLASSES : classes;
908 static struct output_criteria_match *
909 get_criteria_match (const char **arg)
911 struct output_criteria *c = get_criteria ();
912 if ((*arg)[0] == '^')
922 parse_commands (const char *arg)
924 struct output_criteria_match *cm = get_criteria_match (&arg);
925 string_array_parse (&cm->commands, ss_cstr (arg), ss_cstr (","));
929 parse_subtypes (const char *arg)
931 struct output_criteria_match *cm = get_criteria_match (&arg);
932 string_array_parse (&cm->subtypes, ss_cstr (arg), ss_cstr (","));
936 parse_labels (const char *arg)
938 struct output_criteria_match *cm = get_criteria_match (&arg);
939 string_array_parse (&cm->labels, ss_cstr (arg), ss_cstr (","));
943 parse_instances (char *arg)
945 struct output_criteria *c = get_criteria ();
946 size_t allocated_instances = c->n_instances;
948 for (char *token = strtok (arg, ","); token; token = strtok (NULL, ","))
950 if (c->n_instances >= allocated_instances)
951 c->instances = x2nrealloc (c->instances, &allocated_instances,
952 sizeof *c->instances);
954 c->instances[c->n_instances++] = (!strcmp (token, "last") ? -1
960 parse_nth_commands (char *arg)
962 struct output_criteria *c = get_criteria ();
963 size_t allocated_commands = c->n_commands;
965 for (char *token = strtok (arg, ","); token; token = strtok (NULL, ","))
967 if (c->n_commands >= allocated_commands)
968 c->commands = x2nrealloc (c->commands, &allocated_commands,
969 sizeof *c->commands);
971 c->commands[c->n_commands++] = atoi (token);
976 parse_members (const char *arg)
978 struct output_criteria *cm = get_criteria ();
979 string_array_parse (&cm->members, ss_cstr (arg), ss_cstr (","));
983 parse_table_look (const char *arg)
985 pivot_table_look_unref (table_look);
987 char *error_s = pivot_table_look_read (arg, &table_look);
989 error (1, 0, "%s", error_s);
993 parse_options (int argc, char *argv[])
999 OPT_MEMBER_NAMES = UCHAR_MAX + 1,
1017 static const struct option long_options[] =
1019 /* Input selection options. */
1020 { "show-hidden", no_argument, NULL, OPT_SHOW_HIDDEN },
1021 { "select", required_argument, NULL, OPT_SELECT },
1022 { "commands", required_argument, NULL, OPT_COMMANDS },
1023 { "nth-commands", required_argument, NULL, OPT_NTH_COMMANDS },
1024 { "subtypes", required_argument, NULL, OPT_SUBTYPES },
1025 { "labels", required_argument, NULL, OPT_LABELS },
1026 { "instances", required_argument, NULL, OPT_INSTANCES },
1027 { "members", required_argument, NULL, OPT_MEMBERS },
1028 { "errors", no_argument, NULL, OPT_ERRORS },
1029 { "or", no_argument, NULL, OPT_OR },
1031 /* "dir" command options. */
1032 { "member-names", no_argument, NULL, OPT_MEMBER_NAMES },
1034 /* "convert" command options. */
1035 { "force", no_argument, NULL, 'f' },
1036 { "table-look", required_argument, NULL, OPT_TABLE_LOOK },
1038 /* "dump-light-table" command options. */
1039 { "sort", no_argument, NULL, OPT_SORT },
1040 { "raw", no_argument, NULL, OPT_RAW },
1042 /* "strings" command options. */
1043 { "no-ascii-only", no_argument, NULL, OPT_NO_ASCII_ONLY },
1044 { "utf8-only", no_argument, NULL, OPT_UTF8_ONLY },
1046 { "help", no_argument, NULL, 'h' },
1047 { "help-developer", no_argument, NULL, OPT_HELP_DEVELOPER },
1048 { "version", no_argument, NULL, 'v' },
1050 { NULL, 0, NULL, 0 },
1055 c = getopt_long (argc, argv, "O:hvf", long_options, NULL);
1062 output_driver_parse_option (optarg, &output_options);
1065 case OPT_MEMBER_NAMES:
1066 show_member_names = true;
1069 case OPT_SHOW_HIDDEN:
1070 get_criteria ()->include_hidden = true;
1074 parse_select (optarg);
1078 parse_commands (optarg);
1081 case OPT_NTH_COMMANDS:
1082 parse_nth_commands (optarg);
1086 parse_subtypes (optarg);
1090 parse_labels (optarg);
1094 parse_instances (optarg);
1098 parse_members (optarg);
1102 get_criteria ()->error = true;
1106 new_criteria = true;
1117 case OPT_TABLE_LOOK:
1118 parse_table_look (optarg);
1121 case OPT_NO_ASCII_ONLY:
1122 exclude_ascii_only = true;
1126 include_utf8_only = true;
1134 version_etc (stdout, "pspp-output", PACKAGE_NAME, PACKAGE_VERSION,
1135 "Ben Pfaff", "John Darrington", NULL_SENTINEL);
1136 exit (EXIT_SUCCESS);
1140 exit (EXIT_SUCCESS);
1142 case OPT_HELP_DEVELOPER:
1144 exit (EXIT_SUCCESS);
1147 exit (EXIT_FAILURE);
1155 struct string s = DS_EMPTY_INITIALIZER;
1156 struct string_set formats = STRING_SET_INITIALIZER(formats);
1157 output_get_supported_formats (&formats);
1159 const struct string_set_node *node;
1160 STRING_SET_FOR_EACH (format, node, &formats)
1162 if (!ds_is_empty (&s))
1163 ds_put_byte (&s, ' ');
1164 ds_put_cstr (&s, format);
1166 string_set_destroy (&formats);
1169 %s, a utility for working with SPSS viewer (.spv) files.\n\
1170 Usage: %s [OPTION]... COMMAND ARG...\n\
1172 The following commands are available:\n\
1173 detect FILE Detect whether FILE is an SPV file.\n\
1174 dir FILE List tables and other items in FILE.\n\
1175 convert SOURCE DEST Convert .spv SOURCE to DEST.\n\
1176 get-table-look SOURCE DEST Copies first selected TableLook into DEST\n\
1177 convert-table-look SOURCE DEST Copies .tlo or .stt SOURCE into .stt DEST\n\
1179 Input selection options for \"dir\" and \"convert\":\n\
1180 --select=CLASS... include only some kinds of objects\n\
1181 --select=help print known object classes\n\
1182 --commands=COMMAND... include only specified COMMANDs\n\
1183 --nth-commands=N... include only the Nth instance of selected commands\n\
1184 --subtypes=SUBTYPE... include only specified SUBTYPEs of output\n\
1185 --labels=LABEL... include only output objects with the given LABELs\n\
1186 --instances=INSTANCE... include only the given object INSTANCEs\n\
1187 --show-hidden include hidden output objects\n\
1188 --or separate two sets of selection options\n\
1190 \"convert\" by default infers the destination's format from its extension.\n\
1191 The known extensions are: %s\n\
1192 The following options override \"convert\" behavior:\n\
1193 -O format=FORMAT set destination format to FORMAT\n\
1194 -O OPTION=VALUE set output option\n\
1195 -f, --force keep output file even given errors\n\
1196 --table-look=FILE override tables' style with TableLook from FILE\n\
1198 --help display this help and exit\n\
1199 --help-developer display help for developer commands and exit\n\
1200 --version output version information and exit\n",
1201 program_name, program_name, ds_cstr (&s));
1206 developer_usage (void)
1209 The following developer commands are available:\n\
1210 dump FILE Dump pivot table structure\n\
1211 [--raw | --sort] dump-light-table FILE Dump light tables\n\
1212 [--raw] dump-legacy-data FILE Dump legacy table data\n\
1213 dump-legacy-table FILE [XPATH]... Dump legacy table XML\n\
1214 dump-structure FILE [XPATH]... Dump structure XML\n\
1215 is-legacy FILE Exit with status 0 if any legacy table selected\n\
1216 strings FILE Dump analysis of strings\n\
1218 Additional input selection options:\n\
1219 --members=MEMBER... include only objects with these Zip member names\n\
1220 --errors include only objects that cannot be loaded\n\
1222 Additional options for \"dir\" command:\n\
1223 --member-names show Zip member names with objects\n\
1225 Options for the \"strings\" command:\n\
1226 --raw Dump all (unique) strings\n\
1227 --raw --no-ascii-only Dump all strings that contain non-ASCII characters\n\
1228 --raw --utf8-only Dump all non-ASCII strings that are valid UTF-8\n\
1231 --raw print raw binary data instead of a parsed version\n\
1232 --sort sort borders and areas for shorter \"diff\" output\n");