spv-file-format: Better document MANIFEST.MF.
[pspp.git] / utilities / pspp-output.c
blobf9f149bd6360299567a559d99b26b613a6860cdc
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/>. */
17 #include <config.h>
19 #include <getopt.h>
20 #include <limits.h>
21 #include <stdlib.h>
22 #include <unistd.h>
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"
44 #include "gl/error.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>
53 #include "gettext.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. */
71 static bool sort;
73 /* --raw: Dump raw binary data in dump-light-table. */
74 static bool raw;
76 /* -f, --force: Keep output file even on error. */
77 static bool force;
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);
88 static void
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;
95 char *s = (x && b
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:
104 break;
106 case SPV_ITEM_TEXT:
107 spv_text_submit (item);
108 break;
110 case SPV_ITEM_TABLE:
111 pivot_table_submit (pivot_table_ref (spv_item_get_table (item)));
112 break;
114 case SPV_ITEM_GRAPH:
115 break;
117 case SPV_ITEM_MODEL:
118 break;
120 case SPV_ITEM_OBJECT:
121 break;
123 case SPV_ITEM_TREE:
124 break;
126 default:
127 abort ();
131 static void
132 print_item_directory (const struct spv_item *item)
134 for (int i = 1; i < spv_item_get_level (item); i++)
135 printf (" ");
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);
141 if (label)
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);
152 free (title);
155 const char *command_id = spv_item_get_command_id (item);
156 if (command_id)
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);
174 putchar ('\n');
177 static void
178 run_detect (int argc UNUSED, char **argv)
180 char *err = spv_detect (argv[1]);
181 if (err)
182 error (1, 0, "%s", err);
185 static void
186 run_directory (int argc UNUSED, char **argv)
188 struct spv_reader *spv;
189 char *err = spv_open (argv[1], &spv);
190 if (err)
191 error (1, 0, "%s", err);
193 struct spv_item **items;
194 size_t n_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]);
198 free (items);
200 spv_close (spv);
203 struct item_path
205 const struct spv_item **nodes;
206 size_t n;
208 #define N_STUB 10
209 const struct spv_item *stub[N_STUB];
212 static void
213 swap_nodes (const struct spv_item **a, const struct spv_item **b)
215 const struct spv_item *tmp = *a;
216 *a = *b;
217 *b = tmp;
220 static void
221 get_path (const struct spv_item *item, struct item_path *path)
223 size_t allocated = 10;
224 path->nodes = path->stub;
225 path->n = 0;
227 while (item)
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;
237 item = item->parent;
240 for (size_t i = 0; i < path->n / 2; i++)
241 swap_nodes (&path->nodes[i], &path->nodes[path->n - i - 1]);
244 static void
245 free_path (struct item_path *path)
247 if (path && path->nodes != path->stub)
248 free (path->nodes);
251 static void
252 dump_heading_transition (const struct spv_item *old,
253 const struct spv_item *new)
255 if (old == new)
256 return;
258 struct item_path old_path, new_path;
259 get_path (old, &old_path);
260 get_path (new, &new_path);
262 size_t common = 0;
263 for (; common < old_path.n && common < new_path.n; common++)
264 if (old_path.nodes[common] != new_path.nodes[common])
265 break;
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);
277 static void
278 run_convert (int argc UNUSED, char **argv)
280 struct spv_reader *spv;
281 char *err = spv_open (argv[1], &spv);
282 if (err)
283 error (1, 0, "%s", err);
285 if (table_look)
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);
292 if (!driver)
293 exit (EXIT_FAILURE);
294 output_driver_register (driver);
296 const struct page_setup *ps = spv_get_page_setup (spv);
297 if (ps)
298 page_setup_item_submit (page_setup_item_create (ps));
300 struct spv_item **items;
301 size_t n_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));
313 free (items);
315 spv_close (spv);
317 output_engine_pop ();
318 fh_done ();
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. */
324 unlink (argv[2]);
328 static const struct pivot_table *
329 get_first_table (const struct spv_reader *spv)
331 struct spv_item **items;
332 size_t n_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]))
338 free (items);
339 return spv_item_get_table (items[i]);
342 free (items);
343 return NULL;
346 static void
347 run_get_table_look (int argc UNUSED, char **argv)
349 struct spv_reader *spv;
350 char *err = spv_open (argv[1], &spv);
351 if (err)
352 error (1, 0, "%s", err);
354 const struct pivot_table *table = get_first_table (spv);
355 if (!table)
356 error (1, 0, "%s: no tables found", argv[1]);
358 err = spv_table_look_write (argv[2], pivot_table_get_look (table));
359 if (err)
360 error (1, 0, "%s", err);
362 spv_close (spv);
365 static void
366 run_convert_table_look (int argc UNUSED, char **argv)
368 struct pivot_table_look *look;
369 char *err = spv_table_look_read (argv[1], &look);
370 if (err)
371 error (1, 0, "%s", err);
373 err = spv_table_look_write (argv[2], look);
374 if (err)
375 error (1, 0, "%s", err);
377 pivot_table_look_unref (look);
378 free (look);
381 static void
382 run_dump (int argc UNUSED, char **argv)
384 struct spv_reader *spv;
385 char *err = spv_open (argv[1], &spv);
386 if (err)
387 error (1, 0, "%s", err);
389 struct spv_item **items;
390 size_t n_items;
391 spv_select (spv, criteria, n_criteria, &items, &n_items);
392 for (size_t i = 0; i < n_items; i++)
393 if (items[i]->type == SPV_ITEM_TABLE)
395 pivot_table_dump (spv_item_get_table (items[i]), 0);
396 putchar ('\n');
398 free (items);
400 spv_close (spv);
403 static int
404 compare_borders (const void *a_, const void *b_)
406 const struct spvlb_border *const *ap = a_;
407 const struct spvlb_border *const *bp = b_;
408 uint32_t a = (*ap)->border_type;
409 uint32_t b = (*bp)->border_type;
411 return a < b ? -1 : a > b;
414 static int
415 compare_cells (const void *a_, const void *b_)
417 const struct spvlb_cell *const *ap = a_;
418 const struct spvlb_cell *const *bp = b_;
419 uint64_t a = (*ap)->index;
420 uint64_t b = (*bp)->index;
422 return a < b ? -1 : a > b;
425 static void
426 run_dump_light_table (int argc UNUSED, char **argv)
428 if (raw && isatty (STDOUT_FILENO))
429 error (1, 0, "not writing binary data to tty");
431 struct spv_reader *spv;
432 char *err = spv_open (argv[1], &spv);
433 if (err)
434 error (1, 0, "%s", err);
436 struct spv_item **items;
437 size_t n_items;
438 spv_select (spv, criteria, n_criteria, &items, &n_items);
439 for (size_t i = 0; i < n_items; i++)
441 if (!spv_item_is_light_table (items[i]))
442 continue;
444 char *error;
445 if (raw)
447 void *data;
448 size_t size;
449 error = spv_item_get_raw_light_table (items[i], &data, &size);
450 if (!error)
452 fwrite (data, size, 1, stdout);
453 free (data);
456 else
458 struct spvlb_table *table;
459 error = spv_item_get_light_table (items[i], &table);
460 if (!error)
462 if (sort)
464 qsort (table->borders->borders, table->borders->n_borders,
465 sizeof *table->borders->borders, compare_borders);
466 qsort (table->cells->cells, table->cells->n_cells,
467 sizeof *table->cells->cells, compare_cells);
469 spvlb_print_table (items[i]->bin_member, 0, table);
470 spvlb_free_table (table);
473 if (error)
475 msg (ME, "%s", error);
476 free (error);
480 free (items);
482 spv_close (spv);
485 static void
486 run_dump_legacy_data (int argc UNUSED, char **argv)
488 struct spv_reader *spv;
489 char *err = spv_open (argv[1], &spv);
490 if (err)
491 error (1, 0, "%s", err);
493 struct spv_item **items;
494 size_t n_items;
495 spv_select (spv, criteria, n_criteria, &items, &n_items);
496 for (size_t i = 0; i < n_items; i++)
497 if (spv_item_is_legacy_table (items[i]))
499 struct spv_data data;
500 char *error;
501 if (raw)
503 void *data;
504 size_t size;
505 error = spv_item_get_raw_legacy_data (items[i], &data, &size);
506 if (!error)
508 fwrite (data, size, 1, stdout);
509 free (data);
512 else
514 error = spv_item_get_legacy_data (items[i], &data);
515 if (!error)
517 printf ("%s:\n", items[i]->bin_member);
518 spv_data_dump (&data, stdout);
519 spv_data_uninit (&data);
520 printf ("\n");
524 if (error)
526 msg (ME, "%s", error);
527 free (error);
530 free (items);
532 spv_close (spv);
535 /* This is really bogus.
537 XPath doesn't have any notion of a default XML namespace, but all of the
538 elements in the documents we're interested in have a namespace. Thus, we'd
539 need to require the XPath expressions to have a namespace on every single
540 element: vis:sourceVariable, vis:graph, and so on. That's a pain. So,
541 instead, we remove the default namespace from everyplace it occurs. XPath
542 does support the null namespace, so this allows sourceVariable, graph,
543 etc. to work.
545 See http://plasmasturm.org/log/259/ and
546 https://mail.gnome.org/archives/xml/2003-April/msg00144.html for more
547 information.*/
548 static void
549 remove_default_xml_namespace (xmlNode *node)
551 if (node->ns && !node->ns->prefix)
552 node->ns = NULL;
554 for (xmlNode *child = node->children; child; child = child->next)
555 remove_default_xml_namespace (child);
558 static void
559 register_ns (xmlXPathContext *ctx, const char *prefix, const char *uri)
561 xmlXPathRegisterNs (ctx, CHAR_CAST (xmlChar *, prefix),
562 CHAR_CAST (xmlChar *, uri));
565 static xmlXPathContext *
566 create_xpath_context (xmlDoc *doc)
568 xmlXPathContext *ctx = xmlXPathNewContext (doc);
569 register_ns (ctx, "vgr", "http://xml.spss.com/spss/viewer/viewer-graph");
570 register_ns (ctx, "vizml", "http://xml.spss.com/visualization");
571 register_ns (ctx, "vmd", "http://xml.spss.com/spss/viewer/viewer-model");
572 register_ns (ctx, "vps", "http://xml.spss.com/spss/viewer/viewer-pagesetup");
573 register_ns (ctx, "vst", "http://xml.spss.com/spss/viewer/viewer-style");
574 register_ns (ctx, "vtb", "http://xml.spss.com/spss/viewer/viewer-table");
575 register_ns (ctx, "vtl", "http://xml.spss.com/spss/viewer/table-looks");
576 register_ns (ctx, "vtt", "http://xml.spss.com/spss/viewer/viewer-treemodel");
577 register_ns (ctx, "vtx", "http://xml.spss.com/spss/viewer/viewer-text");
578 register_ns (ctx, "xsi", "http://www.w3.org/2001/XMLSchema-instance");
579 return ctx;
582 static void
583 dump_xml (int argc, char **argv, const char *member_name,
584 char *error_s, xmlDoc *doc)
586 if (!error_s)
588 if (argc == 2)
590 printf ("<!-- %s -->\n", member_name);
591 xmlElemDump (stdout, NULL, xmlDocGetRootElement (doc));
592 putchar ('\n');
594 else
596 bool any_results = false;
598 remove_default_xml_namespace (xmlDocGetRootElement (doc));
599 for (int i = 2; i < argc; i++)
601 xmlXPathContext *xpath_ctx = create_xpath_context (doc);
602 xmlXPathSetContextNode (xmlDocGetRootElement (doc),
603 xpath_ctx);
604 xmlXPathObject *xpath_obj = xmlXPathEvalExpression(
605 CHAR_CAST (xmlChar *, argv[i]), xpath_ctx);
606 if (!xpath_obj)
607 error (1, 0, _("%s: invalid XPath expression"), argv[i]);
609 const xmlNodeSet *nodes = xpath_obj->nodesetval;
610 if (nodes && nodes->nodeNr > 0)
612 if (!any_results)
614 printf ("<!-- %s -->\n", member_name);
615 any_results = true;
617 for (size_t j = 0; j < nodes->nodeNr; j++)
619 xmlElemDump (stdout, doc, nodes->nodeTab[j]);
620 putchar ('\n');
624 xmlXPathFreeObject (xpath_obj);
625 xmlXPathFreeContext (xpath_ctx);
627 if (any_results)
628 putchar ('\n');;
630 xmlFreeDoc (doc);
632 else
634 printf ("<!-- %s -->\n", member_name);
635 msg (ME, "%s", error_s);
636 free (error_s);
640 static void
641 run_dump_legacy_table (int argc, char **argv)
643 struct spv_reader *spv;
644 char *err = spv_open (argv[1], &spv);
645 if (err)
646 error (1, 0, "%s", err);
648 struct spv_item **items;
649 size_t n_items;
650 spv_select (spv, criteria, n_criteria, &items, &n_items);
651 for (size_t i = 0; i < n_items; i++)
652 if (spv_item_is_legacy_table (items[i]))
654 xmlDoc *doc;
655 char *error_s = spv_item_get_legacy_table (items[i], &doc);
656 dump_xml (argc, argv, items[i]->xml_member, error_s, doc);
658 free (items);
660 spv_close (spv);
663 static void
664 run_dump_structure (int argc, char **argv)
666 struct spv_reader *spv;
667 char *err = spv_open (argv[1], &spv);
668 if (err)
669 error (1, 0, "%s", err);
671 struct spv_item **items;
672 size_t n_items;
673 spv_select (spv, criteria, n_criteria, &items, &n_items);
674 const char *last_structure_member = NULL;
675 for (size_t i = 0; i < n_items; i++)
676 if (!last_structure_member || strcmp (items[i]->structure_member,
677 last_structure_member))
679 last_structure_member = items[i]->structure_member;
681 xmlDoc *doc;
682 char *error_s = spv_item_get_structure (items[i], &doc);
683 dump_xml (argc, argv, items[i]->structure_member, error_s, doc);
685 free (items);
687 spv_close (spv);
690 static void
691 run_is_legacy (int argc UNUSED, char **argv)
693 struct spv_reader *spv;
694 char *err = spv_open (argv[1], &spv);
695 if (err)
696 error (1, 0, "%s", err);
698 bool is_legacy = false;
700 struct spv_item **items;
701 size_t n_items;
702 spv_select (spv, criteria, n_criteria, &items, &n_items);
703 for (size_t i = 0; i < n_items; i++)
704 if (spv_item_is_legacy_table (items[i]))
706 is_legacy = true;
707 break;
709 free (items);
711 spv_close (spv);
713 exit (is_legacy ? EXIT_SUCCESS : EXIT_FAILURE);
716 struct command
718 const char *name;
719 int min_args, max_args;
720 void (*run) (int argc, char **argv);
723 static const struct command commands[] =
725 { "detect", 1, 1, run_detect },
726 { "dir", 1, 1, run_directory },
727 { "convert", 2, 2, run_convert },
728 { "get-table-look", 2, 2, run_get_table_look },
729 { "convert-table-look", 2, 2, run_convert_table_look },
731 /* Undocumented commands. */
732 { "dump", 1, 1, run_dump },
733 { "dump-light-table", 1, 1, run_dump_light_table },
734 { "dump-legacy-data", 1, 1, run_dump_legacy_data },
735 { "dump-legacy-table", 1, INT_MAX, run_dump_legacy_table },
736 { "dump-structure", 1, INT_MAX, run_dump_structure },
737 { "is-legacy", 1, 1, run_is_legacy },
739 static const int n_commands = sizeof commands / sizeof *commands;
741 static const struct command *
742 find_command (const char *name)
744 for (size_t i = 0; i < n_commands; i++)
746 const struct command *c = &commands[i];
747 if (!strcmp (name, c->name))
748 return c;
750 return NULL;
753 static void
754 emit_msg (const struct msg *m, void *aux UNUSED)
756 if (m->severity == MSG_S_ERROR || m->severity == MSG_S_WARNING)
757 n_warnings++;
759 char *s = msg_to_string (m);
760 fprintf (stderr, "%s\n", s);
761 free (s);
765 main (int argc, char **argv)
767 set_program_name (argv[0]);
768 msg_set_handler (emit_msg, NULL);
769 settings_init ();
770 i18n_init ();
772 parse_options (argc, argv);
774 argc -= optind;
775 argv += optind;
777 if (argc < 1)
778 error (1, 0, _("missing command name (use --help for help)"));
780 const struct command *c = find_command (argv[0]);
781 if (!c)
782 error (1, 0, _("unknown command \"%s\" (use --help for help)"), argv[0]);
784 int n_args = argc - 1;
785 if (n_args < c->min_args || n_args > c->max_args)
787 if (c->min_args == c->max_args)
789 error (1, 0,
790 ngettext ("\"%s\" command takes exactly %d argument",
791 "\"%s\" command takes exactly %d arguments",
792 c->min_args), c->name, c->min_args);
794 else if (c->max_args == INT_MAX)
796 error (1, 0,
797 ngettext ("\"%s\" command requires at least %d argument",
798 "\"%s\" command requires at least %d arguments",
799 c->min_args), c->name, c->min_args);
801 else
803 error (1, 0,
804 _("\"%s\" command requires between %d and %d arguments"),
805 c->name, c->min_args, c->max_args);
809 c->run (argc, argv);
811 pivot_table_look_unref (table_look);
812 i18n_done ();
814 return n_warnings ? EXIT_FAILURE : EXIT_SUCCESS;
817 static struct spv_criteria *
818 get_criteria (void)
820 if (!n_criteria || new_criteria)
822 new_criteria = false;
823 if (n_criteria >= allocated_criteria)
824 criteria = x2nrealloc (criteria, &allocated_criteria,
825 sizeof *criteria);
826 criteria[n_criteria++] = (struct spv_criteria) SPV_CRITERIA_INITIALIZER;
829 return &criteria[n_criteria - 1];
832 static void
833 parse_select (char *arg)
835 bool invert = arg[0] == '^';
836 arg += invert;
838 unsigned classes = 0;
839 for (char *token = strtok (arg, ","); token; token = strtok (NULL, ","))
841 if (!strcmp (arg, "all"))
842 classes = SPV_ALL_CLASSES;
843 else if (!strcmp (arg, "help"))
845 puts (_("The following object classes are supported:"));
846 for (int class = 0; class < SPV_N_CLASSES; class++)
847 printf ("- %s\n", spv_item_class_to_string (class));
848 exit (0);
850 else
852 int class = spv_item_class_from_string (token);
853 if (class == SPV_N_CLASSES)
854 error (1, 0, _("%s: unknown object class (use --select=help "
855 "for help"), arg);
856 classes |= 1u << class;
860 struct spv_criteria *c = get_criteria ();
861 c->classes = invert ? classes ^ SPV_ALL_CLASSES : classes;
864 static struct spv_criteria_match *
865 get_criteria_match (const char **arg)
867 struct spv_criteria *c = get_criteria ();
868 if ((*arg)[0] == '^')
870 (*arg)++;
871 return &c->exclude;
873 else
874 return &c->include;
877 static void
878 parse_commands (const char *arg)
880 struct spv_criteria_match *cm = get_criteria_match (&arg);
881 string_array_parse (&cm->commands, ss_cstr (arg), ss_cstr (","));
884 static void
885 parse_subtypes (const char *arg)
887 struct spv_criteria_match *cm = get_criteria_match (&arg);
888 string_array_parse (&cm->subtypes, ss_cstr (arg), ss_cstr (","));
891 static void
892 parse_labels (const char *arg)
894 struct spv_criteria_match *cm = get_criteria_match (&arg);
895 string_array_parse (&cm->labels, ss_cstr (arg), ss_cstr (","));
898 static void
899 parse_instances (char *arg)
901 struct spv_criteria *c = get_criteria ();
902 size_t allocated_instances = c->n_instances;
904 for (char *token = strtok (arg, ","); token; token = strtok (NULL, ","))
906 if (c->n_instances >= allocated_instances)
907 c->instances = x2nrealloc (c->instances, &allocated_instances,
908 sizeof *c->instances);
910 c->instances[c->n_instances++] = (!strcmp (token, "last") ? -1
911 : atoi (token));
915 static void
916 parse_nth_commands (char *arg)
918 struct spv_criteria *c = get_criteria ();
919 size_t allocated_commands = c->n_commands;
921 for (char *token = strtok (arg, ","); token; token = strtok (NULL, ","))
923 if (c->n_commands >= allocated_commands)
924 c->commands = x2nrealloc (c->commands, &allocated_commands,
925 sizeof *c->commands);
927 c->commands[c->n_commands++] = atoi (token);
931 static void
932 parse_members (const char *arg)
934 struct spv_criteria *cm = get_criteria ();
935 string_array_parse (&cm->members, ss_cstr (arg), ss_cstr (","));
938 static void
939 parse_table_look (const char *arg)
941 pivot_table_look_unref (table_look);
943 char *error_s = spv_table_look_read (arg, &table_look);
944 if (error_s)
945 error (1, 0, "%s", error_s);
948 static void
949 parse_options (int argc, char *argv[])
951 for (;;)
953 enum
955 OPT_MEMBER_NAMES = UCHAR_MAX + 1,
956 OPT_SHOW_HIDDEN,
957 OPT_SELECT,
958 OPT_COMMANDS,
959 OPT_NTH_COMMANDS,
960 OPT_SUBTYPES,
961 OPT_LABELS,
962 OPT_INSTANCES,
963 OPT_MEMBERS,
964 OPT_ERRORS,
965 OPT_OR,
966 OPT_SORT,
967 OPT_RAW,
968 OPT_TABLE_LOOK,
970 static const struct option long_options[] =
972 /* Input selection options. */
973 { "show-hidden", no_argument, NULL, OPT_SHOW_HIDDEN },
974 { "select", required_argument, NULL, OPT_SELECT },
975 { "commands", required_argument, NULL, OPT_COMMANDS },
976 { "nth-commands", required_argument, NULL, OPT_NTH_COMMANDS },
977 { "subtypes", required_argument, NULL, OPT_SUBTYPES },
978 { "labels", required_argument, NULL, OPT_LABELS },
979 { "instances", required_argument, NULL, OPT_INSTANCES },
980 { "members", required_argument, NULL, OPT_MEMBERS },
981 { "errors", no_argument, NULL, OPT_ERRORS },
982 { "or", no_argument, NULL, OPT_OR },
984 /* "dir" command options. */
985 { "member-names", no_argument, NULL, OPT_MEMBER_NAMES },
987 /* "convert" command options. */
988 { "force", no_argument, NULL, 'f' },
989 { "table-look", required_argument, NULL, OPT_TABLE_LOOK },
991 /* "dump-light-table" command options. */
992 { "sort", no_argument, NULL, OPT_SORT },
993 { "raw", no_argument, NULL, OPT_RAW },
995 { "help", no_argument, NULL, 'h' },
996 { "version", no_argument, NULL, 'v' },
998 { NULL, 0, NULL, 0 },
1001 int c;
1003 c = getopt_long (argc, argv, "O:hvf", long_options, NULL);
1004 if (c == -1)
1005 break;
1007 switch (c)
1009 case 'O':
1010 output_driver_parse_option (optarg, &output_options);
1011 break;
1013 case OPT_MEMBER_NAMES:
1014 show_member_names = true;
1015 break;
1017 case OPT_SHOW_HIDDEN:
1018 get_criteria ()->include_hidden = true;
1019 break;
1021 case OPT_SELECT:
1022 parse_select (optarg);
1023 break;
1025 case OPT_COMMANDS:
1026 parse_commands (optarg);
1027 break;
1029 case OPT_NTH_COMMANDS:
1030 parse_nth_commands (optarg);
1031 break;
1033 case OPT_SUBTYPES:
1034 parse_subtypes (optarg);
1035 break;
1037 case OPT_LABELS:
1038 parse_labels (optarg);
1039 break;
1041 case OPT_INSTANCES:
1042 parse_instances (optarg);
1043 break;
1045 case OPT_MEMBERS:
1046 parse_members (optarg);
1047 break;
1049 case OPT_ERRORS:
1050 get_criteria ()->error = true;
1051 break;
1053 case OPT_OR:
1054 new_criteria = true;
1055 break;
1057 case OPT_SORT:
1058 sort = true;
1059 break;
1061 case OPT_RAW:
1062 raw = true;
1063 break;
1065 case OPT_TABLE_LOOK:
1066 parse_table_look (optarg);
1067 break;
1069 case 'f':
1070 force = true;
1071 break;
1073 case 'v':
1074 version_etc (stdout, "pspp-output", PACKAGE_NAME, PACKAGE_VERSION,
1075 "Ben Pfaff", "John Darrington", NULL_SENTINEL);
1076 exit (EXIT_SUCCESS);
1078 case 'h':
1079 usage ();
1080 exit (EXIT_SUCCESS);
1082 default:
1083 exit (EXIT_FAILURE);
1088 static void
1089 usage (void)
1091 struct string s = DS_EMPTY_INITIALIZER;
1092 struct string_set formats = STRING_SET_INITIALIZER(formats);
1093 output_get_supported_formats (&formats);
1094 const char *format;
1095 const struct string_set_node *node;
1096 STRING_SET_FOR_EACH (format, node, &formats)
1098 if (!ds_is_empty (&s))
1099 ds_put_byte (&s, ' ');
1100 ds_put_cstr (&s, format);
1102 string_set_destroy (&formats);
1104 printf ("\
1105 %s, a utility for working with SPSS viewer (.spv) files.\n\
1106 Usage: %s [OPTION]... COMMAND ARG...\n\
1108 The following commands are available:\n\
1109 detect FILE Detect whether FILE is an SPV file.\n\
1110 dir FILE List tables and other items in FILE.\n\
1111 convert SOURCE DEST Convert .spv SOURCE to DEST.\n\
1112 get-table-look SOURCE DEST Copies first selected TableLook into DEST\n\
1113 convert-table-look SOURCE DEST Copies .tlo or .stt SOURCE into DEST\n\
1115 Input selection options for \"dir\" and \"convert\":\n\
1116 --select=CLASS... include only some kinds of objects\n\
1117 --select=help print known object classes\n\
1118 --commands=COMMAND... include only specified COMMANDs\n\
1119 --nth-commands=N... include only the Nth instance of selected commands\n\
1120 --subtypes=SUBTYPE... include only specified SUBTYPEs of output\n\
1121 --labels=LABEL... include only output objects with the given LABELs\n\
1122 --instances=INSTANCE... include only the given object INSTANCEs\n\
1123 --show-hidden include hidden output objects\n\
1124 --or separate two sets of selection options\n\
1126 \"convert\" by default infers the destination's format from its extension.\n\
1127 The known extensions are: %s\n\
1128 The following options override \"convert\" behavior:\n\
1129 -O format=FORMAT set destination format to FORMAT\n\
1130 -O OPTION=VALUE set output option\n\
1131 -f, --force keep output file even given errors\n\
1132 --table-look=FILE override tables' style with TableLook from FILE\n\
1133 Other options:\n\
1134 --help display this help and exit\n\
1135 --version output version information and exit\n",
1136 program_name, program_name, ds_cstr (&s));
1137 ds_destroy (&s);