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 /* --use-page-setup: Use page setup from .spv file rather than command line. */
93 static bool use_page_setup
;
95 /* Number of warnings issued. */
96 static size_t n_warnings
;
98 static void usage (void);
99 static void developer_usage (void);
100 static void parse_options (int argc
, char **argv
);
102 static struct output_item
*
103 annotate_member_names (const struct output_item
*in
)
105 if (in
->type
== OUTPUT_ITEM_GROUP
)
107 struct output_item
*out
= group_item_clone_empty (in
);
108 for (size_t i
= 0; i
< in
->group
.n_children
; i
++)
110 const struct output_item
*item
= in
->group
.children
[i
];
111 const char *members
[4];
112 size_t n
= spv_info_get_members (item
->spv_info
, members
,
113 sizeof members
/ sizeof *members
);
116 struct string s
= DS_EMPTY_INITIALIZER
;
117 ds_put_cstr (&s
, members
[0]);
118 for (size_t i
= 1; i
< n
; i
++)
119 ds_put_format (&s
, " and %s", members
[i
]);
120 group_item_add_child (out
, text_item_create_nocopy (
121 TEXT_ITEM_TITLE
, ds_steal_cstr (&s
),
122 xstrdup ("Member Names")));
125 group_item_add_child (out
, output_item_ref (item
));
130 return output_item_ref (in
);
134 print_item_directory (const struct output_item
*item
, int level
)
136 for (int i
= 0; i
< level
; i
++)
139 printf ("- %s", output_item_type_to_string (item
->type
));
141 const char *label
= output_item_get_label (item
);
143 printf (" \"%s\"", label
);
145 if (item
->type
== OUTPUT_ITEM_TABLE
)
147 char *title
= pivot_value_to_string (item
->table
->title
, item
->table
);
148 if (!label
|| strcmp (title
, label
))
149 printf (" title \"%s\"", title
);
153 if (item
->command_name
)
154 printf (" command \"%s\"", item
->command_name
);
156 char *subtype
= output_item_get_subtype (item
);
159 if (!label
|| strcmp (label
, subtype
))
160 printf (" subtype \"%s\"", subtype
);
165 printf (" (%s)", item
->type
== OUTPUT_ITEM_GROUP
? "collapsed" : "hidden");
167 if (show_member_names
)
169 const char *members
[4];
170 size_t n
= spv_info_get_members (item
->spv_info
, members
,
171 sizeof members
/ sizeof *members
);
173 for (size_t i
= 0; i
< n
; i
++)
174 printf (" %s %s", i
== 0 ? "in" : "and", members
[i
]);
178 if (item
->type
== OUTPUT_ITEM_GROUP
)
179 for (size_t i
= 0; i
< item
->group
.n_children
; i
++)
180 print_item_directory (item
->group
.children
[i
], level
+ 1);
184 run_detect (int argc UNUSED
, char **argv
)
186 char *err
= spv_detect (argv
[1]);
188 error (1, 0, "%s", err
);
191 static struct output_item
*
192 read_and_filter_spv (const char *name
, struct page_setup
**psp
)
194 struct output_item
*root
;
195 char *err
= spv_read (name
, &root
, psp
);
197 error (1, 0, "%s", err
);
198 return output_select (root
, criteria
, n_criteria
);
202 run_directory (int argc UNUSED
, char **argv
)
204 struct output_item
*root
= read_and_filter_spv (argv
[1], NULL
);
205 for (size_t i
= 0; i
< root
->group
.n_children
; i
++)
206 print_item_directory (root
->group
.children
[i
], 0);
207 output_item_unref (root
);
211 set_table_look_recursively (struct output_item
*item
,
212 const struct pivot_table_look
*look
)
214 if (item
->type
== OUTPUT_ITEM_TABLE
)
215 pivot_table_set_look (item
->table
, look
);
216 else if (item
->type
== OUTPUT_ITEM_GROUP
)
217 for (size_t i
= 0; i
< item
->group
.n_children
; i
++)
218 set_table_look_recursively (item
->group
.children
[i
], look
);
222 run_convert (int argc UNUSED
, char **argv
)
224 struct page_setup
*ps
;
225 struct output_item
*root
= read_and_filter_spv (argv
[1], &ps
);
227 set_table_look_recursively (root
, table_look
);
228 if (show_member_names
)
230 struct output_item
*new_root
= annotate_member_names (root
);
231 output_item_unref (root
);
235 output_engine_push ();
236 output_set_filename (argv
[1]);
237 string_map_replace (&output_options
, "output-file", argv
[2]);
238 struct output_driver
*driver
= output_driver_create (&output_options
);
241 output_driver_register (driver
);
246 output_set_page_setup (ps
);
247 page_setup_destroy (ps
);
249 output_item_submit_children (root
);
251 output_engine_pop ();
254 if (n_warnings
&& !force
)
256 /* XXX There could be other files to unlink, e.g. the ascii driver can
257 produce additional files with the charts. */
262 static const struct pivot_table
*
263 get_first_table (const struct output_item
*item
)
265 if (item
->type
== OUTPUT_ITEM_TABLE
)
267 else if (item
->type
== OUTPUT_ITEM_GROUP
)
268 for (size_t i
= 0; i
< item
->group
.n_children
; i
++)
270 const struct pivot_table
*table
271 = get_first_table (item
->group
.children
[i
]);
280 run_get_table_look (int argc UNUSED
, char **argv
)
282 struct pivot_table_look
*look
;
283 if (strcmp (argv
[1], "-"))
285 struct output_item
*root
= read_and_filter_spv (argv
[1], NULL
);
286 const struct pivot_table
*table
= get_first_table (root
);
288 error (1, 0, "%s: no tables found", argv
[1]);
290 look
= pivot_table_look_ref (pivot_table_get_look (table
));
292 output_item_unref (root
);
295 look
= pivot_table_look_ref (pivot_table_look_builtin_default ());
297 char *err
= spv_table_look_write (argv
[2], look
);
299 error (1, 0, "%s", err
);
301 pivot_table_look_unref (look
);
305 run_convert_table_look (int argc UNUSED
, char **argv
)
307 struct pivot_table_look
*look
;
308 char *err
= spv_table_look_read (argv
[1], &look
);
310 error (1, 0, "%s", err
);
312 err
= spv_table_look_write (argv
[2], look
);
314 error (1, 0, "%s", err
);
316 pivot_table_look_unref (look
);
321 run_dump (int argc UNUSED
, char **argv
)
323 struct output_item
*root
= read_and_filter_spv (argv
[1], NULL
);
324 output_item_dump (root
, 0);
325 output_item_unref (root
);
329 compare_borders (const void *a_
, const void *b_
)
331 const struct spvlb_border
*const *ap
= a_
;
332 const struct spvlb_border
*const *bp
= b_
;
333 uint32_t a
= (*ap
)->border_type
;
334 uint32_t b
= (*bp
)->border_type
;
336 return a
< b
? -1 : a
> b
;
340 compare_cells (const void *a_
, const void *b_
)
342 const struct spvlb_cell
*const *ap
= a_
;
343 const struct spvlb_cell
*const *bp
= b_
;
344 uint64_t a
= (*ap
)->index
;
345 uint64_t b
= (*bp
)->index
;
347 return a
< b
? -1 : a
> b
;
350 static char * WARN_UNUSED_RESULT
351 dump_raw (struct zip_reader
*zr
, const char *member_name
)
355 char *error
= zip_member_read_all (zr
, member_name
, &data
, &size
);
358 fwrite (data
, size
, 1, stdout
);
365 dump_light_table (const struct output_item
*item
)
369 error
= dump_raw (item
->spv_info
->zip_reader
,
370 item
->spv_info
->bin_member
);
373 struct spvlb_table
*table
;
374 error
= spv_read_light_table (item
->spv_info
->zip_reader
,
375 item
->spv_info
->bin_member
, &table
);
380 qsort (table
->borders
->borders
, table
->borders
->n_borders
,
381 sizeof *table
->borders
->borders
, compare_borders
);
382 qsort (table
->cells
->cells
, table
->cells
->n_cells
,
383 sizeof *table
->cells
->cells
, compare_cells
);
385 spvlb_print_table (item
->spv_info
->bin_member
, 0, table
);
386 spvlb_free_table (table
);
391 msg (ME
, "%s", error
);
397 run_dump_light_table (int argc UNUSED
, char **argv
)
399 if (raw
&& isatty (STDOUT_FILENO
))
400 error (1, 0, "not writing binary data to tty");
402 struct output_item
*root
= read_and_filter_spv (argv
[1], NULL
);
403 struct output_iterator iter
;
404 OUTPUT_ITEM_FOR_EACH (&iter
, root
)
405 if (iter
.cur
->type
== OUTPUT_ITEM_TABLE
&& !iter
.cur
->spv_info
->xml_member
)
406 dump_light_table (iter
.cur
);
407 output_item_unref (root
);
411 dump_legacy_data (const struct output_item
*item
)
415 error
= dump_raw (item
->spv_info
->zip_reader
,
416 item
->spv_info
->bin_member
);
419 struct spv_data data
;
420 error
= spv_read_legacy_data (item
->spv_info
->zip_reader
,
421 item
->spv_info
->bin_member
, &data
);
424 printf ("%s:\n", item
->spv_info
->bin_member
);
425 spv_data_dump (&data
, stdout
);
426 spv_data_uninit (&data
);
433 msg (ME
, "%s", error
);
439 run_dump_legacy_data (int argc UNUSED
, char **argv
)
441 if (raw
&& isatty (STDOUT_FILENO
))
442 error (1, 0, "not writing binary data to tty");
444 struct output_item
*root
= read_and_filter_spv (argv
[1], NULL
);
445 struct output_iterator iter
;
446 OUTPUT_ITEM_FOR_EACH (&iter
, root
)
447 if (iter
.cur
->type
== OUTPUT_ITEM_TABLE
448 && iter
.cur
->spv_info
->xml_member
449 && iter
.cur
->spv_info
->bin_member
)
450 dump_legacy_data (iter
.cur
);
451 output_item_unref (root
);
454 /* This is really bogus.
456 XPath doesn't have any notion of a default XML namespace, but all of the
457 elements in the documents we're interested in have a namespace. Thus, we'd
458 need to require the XPath expressions to have a namespace on every single
459 element: vis:sourceVariable, vis:graph, and so on. That's a pain. So,
460 instead, we remove the default namespace from everyplace it occurs. XPath
461 does support the null namespace, so this allows sourceVariable, graph,
464 See http://plasmasturm.org/log/259/ and
465 https://mail.gnome.org/archives/xml/2003-April/msg00144.html for more
468 remove_default_xml_namespace (xmlNode
*node
)
470 if (node
->ns
&& !node
->ns
->prefix
)
473 for (xmlNode
*child
= node
->children
; child
; child
= child
->next
)
474 remove_default_xml_namespace (child
);
478 register_ns (xmlXPathContext
*ctx
, const char *prefix
, const char *uri
)
480 xmlXPathRegisterNs (ctx
, CHAR_CAST (xmlChar
*, prefix
),
481 CHAR_CAST (xmlChar
*, uri
));
484 static xmlXPathContext
*
485 create_xpath_context (xmlDoc
*doc
)
487 xmlXPathContext
*ctx
= xmlXPathNewContext (doc
);
488 register_ns (ctx
, "vgr", "http://xml.spss.com/spss/viewer/viewer-graph");
489 register_ns (ctx
, "vizml", "http://xml.spss.com/visualization");
490 register_ns (ctx
, "vmd", "http://xml.spss.com/spss/viewer/viewer-model");
491 register_ns (ctx
, "vps", "http://xml.spss.com/spss/viewer/viewer-pagesetup");
492 register_ns (ctx
, "vst", "http://xml.spss.com/spss/viewer/viewer-style");
493 register_ns (ctx
, "vtb", "http://xml.spss.com/spss/viewer/viewer-table");
494 register_ns (ctx
, "vtl", "http://xml.spss.com/spss/viewer/table-looks");
495 register_ns (ctx
, "vtt", "http://xml.spss.com/spss/viewer/viewer-treemodel");
496 register_ns (ctx
, "vtx", "http://xml.spss.com/spss/viewer/viewer-text");
497 register_ns (ctx
, "xsi", "http://www.w3.org/2001/XMLSchema-instance");
502 dump_xml (int argc
, char **argv
, const char *member_name
,
503 char *error_s
, xmlDoc
*doc
)
509 printf ("<!-- %s -->\n", member_name
);
510 xmlElemDump (stdout
, NULL
, xmlDocGetRootElement (doc
));
515 bool any_results
= false;
517 remove_default_xml_namespace (xmlDocGetRootElement (doc
));
518 for (int i
= 2; i
< argc
; i
++)
520 xmlXPathContext
*xpath_ctx
= create_xpath_context (doc
);
521 xmlXPathSetContextNode (xmlDocGetRootElement (doc
),
523 xmlXPathObject
*xpath_obj
= xmlXPathEvalExpression(
524 CHAR_CAST (xmlChar
*, argv
[i
]), xpath_ctx
);
526 error (1, 0, _("%s: invalid XPath expression"), argv
[i
]);
528 const xmlNodeSet
*nodes
= xpath_obj
->nodesetval
;
529 if (nodes
&& nodes
->nodeNr
> 0)
533 printf ("<!-- %s -->\n", member_name
);
536 for (size_t j
= 0; j
< nodes
->nodeNr
; j
++)
538 xmlElemDump (stdout
, doc
, nodes
->nodeTab
[j
]);
543 xmlXPathFreeObject (xpath_obj
);
544 xmlXPathFreeContext (xpath_ctx
);
553 printf ("<!-- %s -->\n", member_name
);
554 msg (ME
, "%s", error_s
);
560 dump_legacy_table (int argc
, char **argv
, const struct output_item
*item
)
563 char *error_s
= spv_read_xml_member (item
->spv_info
->zip_reader
,
564 item
->spv_info
->xml_member
,
565 false, "visualization", &doc
);
566 dump_xml (argc
, argv
, item
->spv_info
->xml_member
, error_s
, doc
);
570 run_dump_legacy_table (int argc
, char **argv
)
572 struct output_item
*root
= read_and_filter_spv (argv
[1], NULL
);
573 struct output_iterator iter
;
574 OUTPUT_ITEM_FOR_EACH (&iter
, root
)
575 if (iter
.cur
->type
== OUTPUT_ITEM_TABLE
576 && iter
.cur
->spv_info
->xml_member
)
577 dump_legacy_table (argc
, argv
, iter
.cur
);
578 output_item_unref (root
);
582 dump_structure (int argc
, char **argv
, const struct output_item
*item
)
585 char *error_s
= spv_read_xml_member (item
->spv_info
->zip_reader
,
586 item
->spv_info
->structure_member
,
587 true, "heading", &doc
);
588 dump_xml (argc
, argv
, item
->spv_info
->structure_member
, error_s
, doc
);
592 run_dump_structure (int argc
, char **argv
)
594 struct output_item
*root
= read_and_filter_spv (argv
[1], NULL
);
596 const char *last_structure_member
= NULL
;
597 struct output_iterator iter
;
598 OUTPUT_ITEM_FOR_EACH (&iter
, root
)
600 const struct output_item
*item
= iter
.cur
;
601 if (item
->spv_info
->structure_member
602 && (!last_structure_member
603 || strcmp (item
->spv_info
->structure_member
,
604 last_structure_member
)))
606 last_structure_member
= item
->spv_info
->structure_member
;
607 dump_structure (argc
, argv
, item
);
610 output_item_unref (root
);
614 is_any_legacy (const struct output_item
*item
)
616 if (item
->type
== OUTPUT_ITEM_TABLE
)
617 return item
->spv_info
->xml_member
!= NULL
;
618 else if (item
->type
== OUTPUT_ITEM_GROUP
)
619 for (size_t i
= 0; i
< item
->group
.n_children
; i
++)
620 if (is_any_legacy (item
->group
.children
[i
]))
627 run_is_legacy (int argc UNUSED
, char **argv
)
629 struct output_item
*root
= read_and_filter_spv (argv
[1], NULL
);
630 bool is_legacy
= is_any_legacy (root
);
631 output_item_unref (root
);
633 exit (is_legacy
? EXIT_SUCCESS
: EXIT_FAILURE
);
637 is_all_ascii (const char *s
)
640 if (!encoding_guess_is_ascii_text (*s
))
647 dump_strings (const char *encoding
, struct string_array
*strings
)
649 string_array_sort (strings
);
650 string_array_uniq (strings
);
654 if (exclude_ascii_only
|| include_utf8_only
)
657 for (size_t j
= 0; j
< strings
->n
; j
++)
659 char *s
= strings
->strings
[j
];
660 bool is_ascii
= is_all_ascii (s
);
661 bool is_utf8
= !u8_check (CHAR_CAST (uint8_t *, s
), strlen (s
));
662 if (!is_ascii
&& (!include_utf8_only
|| is_utf8
))
663 strings
->strings
[i
++] = s
;
669 for (size_t i
= 0; i
< strings
->n
; i
++)
670 puts (strings
->strings
[i
]);
674 size_t n_nonascii
= 0;
676 for (size_t i
= 0; i
< strings
->n
; i
++)
678 const char *s
= strings
->strings
[i
];
679 if (!is_all_ascii (s
))
682 if (!u8_check (CHAR_CAST (uint8_t *, s
), strlen (s
)))
686 printf ("%s: %zu unique strings, %zu non-ASCII, %zu UTF-8.\n",
687 encoding
, strings
->n
, n_nonascii
, n_utf8
);
691 struct encoded_strings
694 struct string_array strings
;
697 struct encoded_strings_table
699 struct encoded_strings
*es
;
704 collect_strings (const struct output_item
*item
,
705 struct encoded_strings_table
*t
)
708 struct spvlb_table
*table
;
709 error
= spv_read_light_table (item
->spv_info
->zip_reader
,
710 item
->spv_info
->bin_member
, &table
);
713 msg (ME
, "%s", error
);
718 const char *table_encoding
= spvlb_table_get_encoding (table
);
720 for (j
= 0; j
< t
->n
; j
++)
721 if (!strcmp (t
->es
[j
].encoding
, table_encoding
))
725 if (t
->n
>= t
->allocated
)
726 t
->es
= x2nrealloc (t
->es
, &t
->allocated
, sizeof *t
->es
);
727 t
->es
[t
->n
++] = (struct encoded_strings
) {
728 .encoding
= xstrdup (table_encoding
),
729 .strings
= STRING_ARRAY_INITIALIZER
,
732 collect_spvlb_strings (table
, &t
->es
[j
].strings
);
736 run_strings (int argc UNUSED
, char **argv
)
738 struct output_item
*root
= read_and_filter_spv (argv
[1], NULL
);
740 struct encoded_strings_table t
= { .es
= NULL
};
741 struct output_iterator iter
;
742 OUTPUT_ITEM_FOR_EACH (&iter
, root
)
744 const struct output_item
*item
= iter
.cur
;
745 if (item
->type
== OUTPUT_ITEM_TABLE
746 && !item
->spv_info
->xml_member
747 && item
->spv_info
->bin_member
)
748 collect_strings (item
, &t
);
751 for (size_t i
= 0; i
< t
.n
; i
++)
753 dump_strings (t
.es
[i
].encoding
, &t
.es
[i
].strings
);
754 free (t
.es
[i
].encoding
);
755 string_array_destroy (&t
.es
[i
].strings
);
759 output_item_unref (root
);
765 int min_args
, max_args
;
766 void (*run
) (int argc
, char **argv
);
769 static const struct command commands
[] =
771 { "detect", 1, 1, run_detect
},
772 { "dir", 1, 1, run_directory
},
773 { "convert", 2, 2, run_convert
},
774 { "get-table-look", 2, 2, run_get_table_look
},
775 { "convert-table-look", 2, 2, run_convert_table_look
},
777 /* Undocumented commands. */
778 { "dump", 1, 1, run_dump
},
779 { "dump-light-table", 1, 1, run_dump_light_table
},
780 { "dump-legacy-data", 1, 1, run_dump_legacy_data
},
781 { "dump-legacy-table", 1, INT_MAX
, run_dump_legacy_table
},
782 { "dump-structure", 1, INT_MAX
, run_dump_structure
},
783 { "is-legacy", 1, 1, run_is_legacy
},
784 { "strings", 1, 1, run_strings
},
786 static const int n_commands
= sizeof commands
/ sizeof *commands
;
788 static const struct command
*
789 find_command (const char *name
)
791 for (size_t i
= 0; i
< n_commands
; i
++)
793 const struct command
*c
= &commands
[i
];
794 if (!strcmp (name
, c
->name
))
801 emit_msg (const struct msg
*m
, void *aux UNUSED
)
803 if (m
->severity
== MSG_S_ERROR
|| m
->severity
== MSG_S_WARNING
)
806 char *s
= msg_to_string (m
);
807 fprintf (stderr
, "%s\n", s
);
812 main (int argc
, char **argv
)
814 set_program_name (argv
[0]);
815 msg_set_handler (&(struct msg_handler
) { .output_msg
= emit_msg
});
819 parse_options (argc
, argv
);
825 error (1, 0, _("missing command name (use --help for help)"));
827 const struct command
*c
= find_command (argv
[0]);
829 error (1, 0, _("unknown command \"%s\" (use --help for help)"), argv
[0]);
831 int n_args
= argc
- 1;
832 if (n_args
< c
->min_args
|| n_args
> c
->max_args
)
834 if (c
->min_args
== c
->max_args
)
837 ngettext ("\"%s\" command takes exactly %d argument",
838 "\"%s\" command takes exactly %d arguments",
839 c
->min_args
), c
->name
, c
->min_args
);
841 else if (c
->max_args
== INT_MAX
)
844 ngettext ("\"%s\" command requires at least %d argument",
845 "\"%s\" command requires at least %d arguments",
846 c
->min_args
), c
->name
, c
->min_args
);
851 _("\"%s\" command requires between %d and %d arguments"),
852 c
->name
, c
->min_args
, c
->max_args
);
858 pivot_table_look_unref (table_look
);
861 return n_warnings
? EXIT_FAILURE
: EXIT_SUCCESS
;
864 static struct output_criteria
*
867 if (!n_criteria
|| new_criteria
)
869 new_criteria
= false;
870 if (n_criteria
>= allocated_criteria
)
871 criteria
= x2nrealloc (criteria
, &allocated_criteria
,
873 criteria
[n_criteria
++]
874 = (struct output_criteria
) OUTPUT_CRITERIA_INITIALIZER
;
877 return &criteria
[n_criteria
- 1];
881 parse_select (char *arg
)
883 bool invert
= arg
[0] == '^';
886 unsigned classes
= 0;
887 for (char *token
= strtok (arg
, ","); token
; token
= strtok (NULL
, ","))
889 if (!strcmp (arg
, "all"))
890 classes
= OUTPUT_ALL_CLASSES
;
891 else if (!strcmp (arg
, "help"))
893 puts (_("The following object classes are supported:"));
894 for (int class = 0; class < OUTPUT_N_CLASSES
; class++)
895 printf ("- %s\n", output_item_class_to_string (class));
900 int class = output_item_class_from_string (token
);
901 if (class == OUTPUT_N_CLASSES
)
902 error (1, 0, _("unknown object class \"%s\" (use --select=help "
904 classes
|= 1u << class;
908 struct output_criteria
*c
= get_criteria ();
909 c
->classes
= invert
? classes
^ OUTPUT_ALL_CLASSES
: classes
;
912 static struct output_criteria_match
*
913 get_criteria_match (const char **arg
)
915 struct output_criteria
*c
= get_criteria ();
916 if ((*arg
)[0] == '^')
926 parse_commands (const char *arg
)
928 struct output_criteria_match
*cm
= get_criteria_match (&arg
);
929 string_array_parse (&cm
->commands
, ss_cstr (arg
), ss_cstr (","));
933 parse_subtypes (const char *arg
)
935 struct output_criteria_match
*cm
= get_criteria_match (&arg
);
936 string_array_parse (&cm
->subtypes
, ss_cstr (arg
), ss_cstr (","));
940 parse_labels (const char *arg
)
942 struct output_criteria_match
*cm
= get_criteria_match (&arg
);
943 string_array_parse (&cm
->labels
, ss_cstr (arg
), ss_cstr (","));
947 parse_instances (char *arg
)
949 struct output_criteria
*c
= get_criteria ();
950 size_t allocated_instances
= c
->n_instances
;
952 for (char *token
= strtok (arg
, ","); token
; token
= strtok (NULL
, ","))
954 if (c
->n_instances
>= allocated_instances
)
955 c
->instances
= x2nrealloc (c
->instances
, &allocated_instances
,
956 sizeof *c
->instances
);
958 c
->instances
[c
->n_instances
++] = (!strcmp (token
, "last") ? -1
964 parse_nth_commands (char *arg
)
966 struct output_criteria
*c
= get_criteria ();
967 size_t allocated_commands
= c
->n_commands
;
969 for (char *token
= strtok (arg
, ","); token
; token
= strtok (NULL
, ","))
971 if (c
->n_commands
>= allocated_commands
)
972 c
->commands
= x2nrealloc (c
->commands
, &allocated_commands
,
973 sizeof *c
->commands
);
975 c
->commands
[c
->n_commands
++] = atoi (token
);
980 parse_members (const char *arg
)
982 struct output_criteria
*cm
= get_criteria ();
983 string_array_parse (&cm
->members
, ss_cstr (arg
), ss_cstr (","));
987 parse_table_look (const char *arg
)
989 pivot_table_look_unref (table_look
);
991 char *error_s
= pivot_table_look_read (arg
, &table_look
);
993 error (1, 0, "%s", error_s
);
997 parse_options (int argc
, char *argv
[])
1003 OPT_MEMBER_NAMES
= UCHAR_MAX
+ 1,
1022 static const struct option long_options
[] =
1024 /* Input selection options. */
1025 { "show-hidden", no_argument
, NULL
, OPT_SHOW_HIDDEN
},
1026 { "select", required_argument
, NULL
, OPT_SELECT
},
1027 { "commands", required_argument
, NULL
, OPT_COMMANDS
},
1028 { "nth-commands", required_argument
, NULL
, OPT_NTH_COMMANDS
},
1029 { "subtypes", required_argument
, NULL
, OPT_SUBTYPES
},
1030 { "labels", required_argument
, NULL
, OPT_LABELS
},
1031 { "instances", required_argument
, NULL
, OPT_INSTANCES
},
1032 { "members", required_argument
, NULL
, OPT_MEMBERS
},
1033 { "errors", no_argument
, NULL
, OPT_ERRORS
},
1034 { "or", no_argument
, NULL
, OPT_OR
},
1036 /* "dir" command options. */
1037 { "member-names", no_argument
, NULL
, OPT_MEMBER_NAMES
},
1039 /* "convert" command options. */
1040 { "force", no_argument
, NULL
, 'f' },
1041 { "table-look", required_argument
, NULL
, OPT_TABLE_LOOK
},
1042 { "use-page-setup", no_argument
, NULL
, OPT_USE_PAGE_SETUP
},
1044 /* "dump-light-table" command options. */
1045 { "sort", no_argument
, NULL
, OPT_SORT
},
1046 { "raw", no_argument
, NULL
, OPT_RAW
},
1048 /* "strings" command options. */
1049 { "no-ascii-only", no_argument
, NULL
, OPT_NO_ASCII_ONLY
},
1050 { "utf8-only", no_argument
, NULL
, OPT_UTF8_ONLY
},
1052 { "help", no_argument
, NULL
, 'h' },
1053 { "help-developer", no_argument
, NULL
, OPT_HELP_DEVELOPER
},
1054 { "version", no_argument
, NULL
, 'v' },
1056 { NULL
, 0, NULL
, 0 },
1061 c
= getopt_long (argc
, argv
, "O:hvf", long_options
, NULL
);
1068 output_driver_parse_option (optarg
, &output_options
);
1071 case OPT_MEMBER_NAMES
:
1072 show_member_names
= true;
1075 case OPT_SHOW_HIDDEN
:
1076 get_criteria ()->include_hidden
= true;
1080 parse_select (optarg
);
1084 parse_commands (optarg
);
1087 case OPT_NTH_COMMANDS
:
1088 parse_nth_commands (optarg
);
1092 parse_subtypes (optarg
);
1096 parse_labels (optarg
);
1100 parse_instances (optarg
);
1104 parse_members (optarg
);
1108 get_criteria ()->error
= true;
1112 new_criteria
= true;
1123 case OPT_TABLE_LOOK
:
1124 parse_table_look (optarg
);
1127 case OPT_USE_PAGE_SETUP
:
1128 use_page_setup
= true;
1131 case OPT_NO_ASCII_ONLY
:
1132 exclude_ascii_only
= true;
1136 include_utf8_only
= true;
1144 version_etc (stdout
, "pspp-output", PACKAGE_NAME
, PACKAGE_VERSION
,
1145 "Ben Pfaff", "John Darrington", NULL_SENTINEL
);
1146 exit (EXIT_SUCCESS
);
1150 exit (EXIT_SUCCESS
);
1152 case OPT_HELP_DEVELOPER
:
1154 exit (EXIT_SUCCESS
);
1157 exit (EXIT_FAILURE
);
1165 struct string s
= DS_EMPTY_INITIALIZER
;
1166 struct string_set formats
= STRING_SET_INITIALIZER(formats
);
1167 output_get_supported_formats (&formats
);
1169 const struct string_set_node
*node
;
1170 STRING_SET_FOR_EACH (format
, node
, &formats
)
1172 if (!ds_is_empty (&s
))
1173 ds_put_byte (&s
, ' ');
1174 ds_put_cstr (&s
, format
);
1176 string_set_destroy (&formats
);
1179 %s, a utility for working with SPSS viewer (.spv) files.\n\
1180 Usage: %s [OPTION]... COMMAND ARG...\n\
1182 The following commands are available:\n\
1183 detect FILE Detect whether FILE is an SPV file.\n\
1184 dir FILE List tables and other items in FILE.\n\
1185 convert SOURCE DEST Convert .spv SOURCE to DEST.\n\
1186 get-table-look SOURCE DEST Copies first selected TableLook into DEST\n\
1187 convert-table-look SOURCE DEST Copies .tlo or .stt SOURCE into .stt DEST\n\
1189 Input selection options for \"dir\" and \"convert\":\n\
1190 --select=CLASS... include only some kinds of objects\n\
1191 --select=help print known object classes\n\
1192 --commands=COMMAND... include only specified COMMANDs\n\
1193 --nth-commands=N... include only the Nth instance of selected commands\n\
1194 --subtypes=SUBTYPE... include only specified SUBTYPEs of output\n\
1195 --labels=LABEL... include only output objects with the given LABELs\n\
1196 --instances=INSTANCE... include only the given object INSTANCEs\n\
1197 --show-hidden include hidden output objects\n\
1198 --or separate two sets of selection options\n\
1200 \"convert\" by default infers the destination's format from its extension.\n\
1201 The known extensions are: %s\n\
1202 The following options override \"convert\" behavior:\n\
1203 -O format=FORMAT set destination format to FORMAT\n\
1204 -O OPTION=VALUE set output option\n\
1205 -f, --force keep output file even given errors\n\
1206 --table-look=FILE override tables' style with TableLook from FILE\n\
1207 --use-page-setup use page setup from SOURCE\n\
1209 --help display this help and exit\n\
1210 --help-developer display help for developer commands and exit\n\
1211 --version output version information and exit\n",
1212 program_name
, program_name
, ds_cstr (&s
));
1217 developer_usage (void)
1220 The following developer commands are available:\n\
1221 dump FILE Dump pivot table structure\n\
1222 [--raw | --sort] dump-light-table FILE Dump light tables\n\
1223 [--raw] dump-legacy-data FILE Dump legacy table data\n\
1224 dump-legacy-table FILE [XPATH]... Dump legacy table XML\n\
1225 dump-structure FILE [XPATH]... Dump structure XML\n\
1226 is-legacy FILE Exit with status 0 if any legacy table selected\n\
1227 strings FILE Dump analysis of strings\n\
1229 Additional input selection options:\n\
1230 --members=MEMBER... include only objects with these Zip member names\n\
1231 --errors include only objects that cannot be loaded\n\
1233 Additional options for \"dir\" command:\n\
1234 --member-names show Zip member names with objects\n\
1236 Options for the \"strings\" command:\n\
1237 --raw Dump all (unique) strings\n\
1238 --raw --no-ascii-only Dump all strings that contain non-ASCII characters\n\
1239 --raw --utf8-only Dump all non-ASCII strings that are valid UTF-8\n\
1242 --raw print raw binary data instead of a parsed version\n\
1243 --sort sort borders and areas for shorter \"diff\" output\n");