ed45a4d148fc9a6ffc0e02457b3cef8a53cfb241
[pspp] / utilities / pspp-output.c
1  /* PSPP - a program for statistical analysis.
2    Copyright (C) 2017, 2018 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
17 #include <config.h>
18
19 #include <cairo.h>
20 #include <getopt.h>
21 #include <limits.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24
25 #include "data/file-handle-def.h"
26 #include "data/settings.h"
27 #include "libpspp/i18n.h"
28 #include "libpspp/message.h"
29 #include "libpspp/string-map.h"
30 #include "libpspp/string-set.h"
31 #include "output/driver.h"
32 #include "output/group-item.h"
33 #include "output/image-item.h"
34 #include "output/page-setup-item.h"
35 #include "output/pivot-table.h"
36 #include "output/spv/light-binary-parser.h"
37 #include "output/spv/spv-legacy-data.h"
38 #include "output/spv/spv-output.h"
39 #include "output/spv/spv-select.h"
40 #include "output/spv/spv-table-look.h"
41 #include "output/spv/spv.h"
42 #include "output/table-item.h"
43 #include "output/text-item.h"
44
45 #include "gl/c-ctype.h"
46 #include "gl/error.h"
47 #include "gl/progname.h"
48 #include "gl/version-etc.h"
49 #include "gl/xalloc.h"
50
51 #include <libxml/tree.h>
52 #include <libxml/xpath.h>
53 #include <libxml/xpathInternals.h>
54
55 #include "gettext.h"
56 #define _(msgid) gettext (msgid)
57
58 /* -O key=value: Output driver options. */
59 static struct string_map output_options
60     = STRING_MAP_INITIALIZER (output_options);
61
62 /* --member-name: Include .zip member name in "dir" output. */
63 static bool show_member_names;
64
65 /* --show-hidden, --select, --commands, ...: Selection criteria. */
66 static struct spv_criteria *criteria;
67 static size_t n_criteria, allocated_criteria;
68
69 /* --or: Add new element to 'criteria' array. */
70 static bool new_criteria;
71
72 /* --sort: Sort members under dump-light-table, to make comparisons easier. */
73 static bool sort;
74
75 /* --raw: Dump raw binary data in dump-light-table. */
76 static bool raw;
77
78 /* -f, --force: Keep output file even on error. */
79 static bool force;
80
81 /* --table-look: TableLook to replace table style for conversion. */
82 static struct pivot_table_look *table_look;
83
84 /* Number of warnings issued. */
85 static size_t n_warnings;
86
87 static void usage (void);
88 static void parse_options (int argc, char **argv);
89
90 static void
91 dump_item (const struct spv_item *item)
92 {
93   if (show_member_names && (item->xml_member || item->bin_member))
94     {
95       const char *x = item->xml_member;
96       const char *b = item->bin_member;
97
98       /* The strings below are not marked for translation because they are only
99          useful to developers. */
100       char *s = (x && b
101                  ? xasprintf ("%s and %s:", x, b)
102                  : xasprintf ("%s:", x ? x : b));
103       text_item_submit (text_item_create_nocopy (TEXT_ITEM_TITLE, s,
104                                                  xstrdup ("Member Names")));
105     }
106
107   switch (spv_item_get_type (item))
108     {
109     case SPV_ITEM_HEADING:
110       break;
111
112     case SPV_ITEM_TEXT:
113       spv_text_submit (item);
114       break;
115
116     case SPV_ITEM_TABLE:
117       pivot_table_submit (pivot_table_ref (spv_item_get_table (item)));
118       break;
119
120     case SPV_ITEM_GRAPH:
121       break;
122
123     case SPV_ITEM_MODEL:
124       break;
125
126     case SPV_ITEM_IMAGE:
127       image_item_submit (image_item_create (cairo_surface_reference (
128                                               spv_item_get_image (item))));
129       break;
130
131     case SPV_ITEM_TREE:
132       break;
133
134     default:
135       abort ();
136     }
137 }
138
139 static void
140 print_item_directory (const struct spv_item *item)
141 {
142   for (int i = 1; i < spv_item_get_level (item); i++)
143     printf ("    ");
144
145   enum spv_item_type type = spv_item_get_type (item);
146   printf ("- %s", spv_item_type_to_string (type));
147
148   const char *label = spv_item_get_label (item);
149   if (label)
150     printf (" \"%s\"", label);
151
152   if (type == SPV_ITEM_TABLE)
153     {
154       const struct pivot_table *table = spv_item_get_table (item);
155       char *title = pivot_value_to_string (table->title, table);
156       if (!label || strcmp (title, label))
157         printf (" title \"%s\"", title);
158       free (title);
159     }
160
161   const char *command_id = spv_item_get_command_id (item);
162   if (command_id)
163     printf (" command \"%s\"", command_id);
164
165   const char *subtype = spv_item_get_subtype (item);
166   if (subtype && (!label || strcmp (label, subtype)))
167     printf (" subtype \"%s\"", subtype);
168
169   if (!spv_item_is_visible (item))
170     printf (" (hidden)");
171
172   if (show_member_names)
173     {
174       const char *members[] = {
175         item->xml_member,
176         item->bin_member,
177         item->png_member,
178       };
179       size_t n = 0;
180
181       for (size_t i = 0; i < sizeof members / sizeof *members; i++)
182         if (members[i])
183           printf (" %s %s", n++ == 0 ? "in" : "and", members[i]);
184     }
185   putchar ('\n');
186 }
187
188 static void
189 run_detect (int argc UNUSED, char **argv)
190 {
191   char *err = spv_detect (argv[1]);
192   if (err)
193     error (1, 0, "%s", err);
194 }
195
196 static void
197 run_directory (int argc UNUSED, char **argv)
198 {
199   struct spv_reader *spv;
200   char *err = spv_open (argv[1], &spv);
201   if (err)
202     error (1, 0, "%s", err);
203
204   struct spv_item **items;
205   size_t n_items;
206   spv_select (spv, criteria, n_criteria, &items, &n_items);
207   for (size_t i = 0; i < n_items; i++)
208     print_item_directory (items[i]);
209   free (items);
210
211   spv_close (spv);
212 }
213
214 struct item_path
215   {
216     const struct spv_item **nodes;
217     size_t n;
218
219 #define N_STUB 10
220     const struct spv_item *stub[N_STUB];
221   };
222
223 static void
224 swap_nodes (const struct spv_item **a, const struct spv_item **b)
225 {
226   const struct spv_item *tmp = *a;
227   *a = *b;
228   *b = tmp;
229 }
230
231 static void
232 get_path (const struct spv_item *item, struct item_path *path)
233 {
234   size_t allocated = 10;
235   path->nodes = path->stub;
236   path->n = 0;
237
238   while (item)
239     {
240       if (path->n >= allocated)
241         {
242           if (path->nodes == path->stub)
243             path->nodes = xmemdup (path->stub, sizeof path->stub);
244           path->nodes = x2nrealloc (path->nodes, &allocated,
245                                     sizeof *path->nodes);
246         }
247       path->nodes[path->n++] = item;
248       item = item->parent;
249     }
250
251   for (size_t i = 0; i < path->n / 2; i++)
252     swap_nodes (&path->nodes[i], &path->nodes[path->n - i - 1]);
253 }
254
255 static void
256 free_path (struct item_path *path)
257 {
258   if (path && path->nodes != path->stub)
259     free (path->nodes);
260 }
261
262 static void
263 dump_heading_transition (const struct spv_item *old,
264                          const struct spv_item *new)
265 {
266   if (old == new)
267     return;
268
269   struct item_path old_path, new_path;
270   get_path (old, &old_path);
271   get_path (new, &new_path);
272
273   size_t common = 0;
274   for (; common < old_path.n && common < new_path.n; common++)
275     if (old_path.nodes[common] != new_path.nodes[common])
276       break;
277
278   for (size_t i = common; i < old_path.n; i++)
279     group_close_item_submit (group_close_item_create ());
280   for (size_t i = common; i < new_path.n; i++)
281     group_open_item_submit (group_open_item_create (
282                               new_path.nodes[i]->command_id,
283                               new_path.nodes[i]->label));
284
285   free_path (&old_path);
286   free_path (&new_path);
287 }
288
289 static void
290 run_convert (int argc UNUSED, char **argv)
291 {
292   struct spv_reader *spv;
293   char *err = spv_open (argv[1], &spv);
294   if (err)
295     error (1, 0, "%s", err);
296
297   if (table_look)
298     spv_item_set_table_look (spv_get_root (spv), table_look);
299
300   output_engine_push ();
301   output_set_filename (argv[1]);
302   string_map_replace (&output_options, "output-file", argv[2]);
303   struct output_driver *driver = output_driver_create (&output_options);
304   if (!driver)
305     exit (EXIT_FAILURE);
306   output_driver_register (driver);
307
308   const struct page_setup *ps = spv_get_page_setup (spv);
309   if (ps)
310     page_setup_item_submit (page_setup_item_create (ps));
311
312   struct spv_item **items;
313   size_t n_items;
314   spv_select (spv, criteria, n_criteria, &items, &n_items);
315   struct spv_item *prev_heading = spv_get_root (spv);
316   for (size_t i = 0; i < n_items; i++)
317     {
318       struct spv_item *heading
319         = items[i]->type == SPV_ITEM_HEADING ? items[i] : items[i]->parent;
320       dump_heading_transition (prev_heading, heading);
321       dump_item (items[i]);
322       prev_heading = heading;
323     }
324   dump_heading_transition (prev_heading, spv_get_root (spv));
325   free (items);
326
327   spv_close (spv);
328
329   output_engine_pop ();
330   fh_done ();
331
332   if (n_warnings && !force)
333     {
334       /* XXX There could be other files to unlink, e.g. the ascii driver can
335          produce additional files with the charts. */
336       unlink (argv[2]);
337     }
338 }
339
340 static const struct pivot_table *
341 get_first_table (const struct spv_reader *spv)
342 {
343   struct spv_item **items;
344   size_t n_items;
345   spv_select (spv, criteria, n_criteria, &items, &n_items);
346
347   for (size_t i = 0; i < n_items; i++)
348     if (spv_item_is_table (items[i]))
349       {
350         free (items);
351         return spv_item_get_table (items[i]);
352       }
353
354   free (items);
355   return NULL;
356 }
357
358 static void
359 run_get_table_look (int argc UNUSED, char **argv)
360 {
361   struct pivot_table_look *look;
362   if (strcmp (argv[1], "-"))
363     {
364       struct spv_reader *spv;
365       char *err = spv_open (argv[1], &spv);
366       if (err)
367         error (1, 0, "%s", err);
368
369       const struct pivot_table *table = get_first_table (spv);
370       if (!table)
371         error (1, 0, "%s: no tables found", argv[1]);
372
373       look = pivot_table_look_ref (pivot_table_get_look (table));
374
375       spv_close (spv);
376     }
377   else
378     look = pivot_table_look_ref (pivot_table_look_builtin_default ());
379
380   char *err = spv_table_look_write (argv[2], look);
381   if (err)
382     error (1, 0, "%s", err);
383
384   pivot_table_look_unref (look);
385 }
386
387 static void
388 run_convert_table_look (int argc UNUSED, char **argv)
389 {
390   struct pivot_table_look *look;
391   char *err = spv_table_look_read (argv[1], &look);
392   if (err)
393     error (1, 0, "%s", err);
394
395   err = spv_table_look_write (argv[2], look);
396   if (err)
397     error (1, 0, "%s", err);
398
399   pivot_table_look_unref (look);
400   free (look);
401 }
402
403 static void
404 run_dump (int argc UNUSED, char **argv)
405 {
406   struct spv_reader *spv;
407   char *err = spv_open (argv[1], &spv);
408   if (err)
409     error (1, 0, "%s", err);
410
411   struct spv_item **items;
412   size_t n_items;
413   spv_select (spv, criteria, n_criteria, &items, &n_items);
414   for (size_t i = 0; i < n_items; i++)
415     if (items[i]->type == SPV_ITEM_TABLE)
416       {
417         pivot_table_dump (spv_item_get_table (items[i]), 0);
418         putchar ('\n');
419       }
420   free (items);
421
422   spv_close (spv);
423 }
424
425 static int
426 compare_borders (const void *a_, const void *b_)
427 {
428   const struct spvlb_border *const *ap = a_;
429   const struct spvlb_border *const *bp = b_;
430   uint32_t a = (*ap)->border_type;
431   uint32_t b = (*bp)->border_type;
432
433   return a < b ? -1 : a > b;
434 }
435
436 static int
437 compare_cells (const void *a_, const void *b_)
438 {
439   const struct spvlb_cell *const *ap = a_;
440   const struct spvlb_cell *const *bp = b_;
441   uint64_t a = (*ap)->index;
442   uint64_t b = (*bp)->index;
443
444   return a < b ? -1 : a > b;
445 }
446
447 static void
448 run_dump_light_table (int argc UNUSED, char **argv)
449 {
450   if (raw && isatty (STDOUT_FILENO))
451     error (1, 0, "not writing binary data to tty");
452
453   struct spv_reader *spv;
454   char *err = spv_open (argv[1], &spv);
455   if (err)
456     error (1, 0, "%s", err);
457
458   struct spv_item **items;
459   size_t n_items;
460   spv_select (spv, criteria, n_criteria, &items, &n_items);
461   for (size_t i = 0; i < n_items; i++)
462     {
463       if (!spv_item_is_light_table (items[i]))
464         continue;
465
466       char *error;
467       if (raw)
468         {
469           void *data;
470           size_t size;
471           error = spv_item_get_raw_light_table (items[i], &data, &size);
472           if (!error)
473             {
474               fwrite (data, size, 1, stdout);
475               free (data);
476             }
477         }
478       else
479         {
480           struct spvlb_table *table;
481           error = spv_item_get_light_table (items[i], &table);
482           if (!error)
483             {
484               if (sort)
485                 {
486                   qsort (table->borders->borders, table->borders->n_borders,
487                          sizeof *table->borders->borders, compare_borders);
488                   qsort (table->cells->cells, table->cells->n_cells,
489                          sizeof *table->cells->cells, compare_cells);
490                 }
491               spvlb_print_table (items[i]->bin_member, 0, table);
492               spvlb_free_table (table);
493             }
494         }
495       if (error)
496         {
497           msg (ME, "%s", error);
498           free (error);
499         }
500     }
501
502   free (items);
503
504   spv_close (spv);
505 }
506
507 static void
508 run_dump_legacy_data (int argc UNUSED, char **argv)
509 {
510   struct spv_reader *spv;
511   char *err = spv_open (argv[1], &spv);
512   if (err)
513     error (1, 0, "%s", err);
514
515   struct spv_item **items;
516   size_t n_items;
517   spv_select (spv, criteria, n_criteria, &items, &n_items);
518   for (size_t i = 0; i < n_items; i++)
519     if (spv_item_is_legacy_table (items[i]))
520       {
521         struct spv_data data;
522         char *error;
523         if (raw)
524           {
525             void *data;
526             size_t size;
527             error = spv_item_get_raw_legacy_data (items[i], &data, &size);
528             if (!error)
529               {
530                 fwrite (data, size, 1, stdout);
531                 free (data);
532               }
533           }
534         else
535           {
536             error = spv_item_get_legacy_data (items[i], &data);
537             if (!error)
538               {
539                 printf ("%s:\n", items[i]->bin_member);
540                 spv_data_dump (&data, stdout);
541                 spv_data_uninit (&data);
542                 printf ("\n");
543               }
544           }
545
546         if (error)
547           {
548             msg (ME, "%s", error);
549             free (error);
550           }
551       }
552   free (items);
553
554   spv_close (spv);
555 }
556
557 /* This is really bogus.
558
559    XPath doesn't have any notion of a default XML namespace, but all of the
560    elements in the documents we're interested in have a namespace.  Thus, we'd
561    need to require the XPath expressions to have a namespace on every single
562    element: vis:sourceVariable, vis:graph, and so on.  That's a pain.  So,
563    instead, we remove the default namespace from everyplace it occurs.  XPath
564    does support the null namespace, so this allows sourceVariable, graph,
565    etc. to work.
566
567    See http://plasmasturm.org/log/259/ and
568    https://mail.gnome.org/archives/xml/2003-April/msg00144.html for more
569    information.*/
570 static void
571 remove_default_xml_namespace (xmlNode *node)
572 {
573   if (node->ns && !node->ns->prefix)
574     node->ns = NULL;
575
576   for (xmlNode *child = node->children; child; child = child->next)
577     remove_default_xml_namespace (child);
578 }
579
580 static void
581 register_ns (xmlXPathContext *ctx, const char *prefix, const char *uri)
582 {
583   xmlXPathRegisterNs (ctx, CHAR_CAST (xmlChar *, prefix),
584                       CHAR_CAST (xmlChar *, uri));
585 }
586
587 static xmlXPathContext *
588 create_xpath_context (xmlDoc *doc)
589 {
590   xmlXPathContext *ctx = xmlXPathNewContext (doc);
591   register_ns (ctx, "vgr", "http://xml.spss.com/spss/viewer/viewer-graph");
592   register_ns (ctx, "vizml", "http://xml.spss.com/visualization");
593   register_ns (ctx, "vmd", "http://xml.spss.com/spss/viewer/viewer-model");
594   register_ns (ctx, "vps", "http://xml.spss.com/spss/viewer/viewer-pagesetup");
595   register_ns (ctx, "vst", "http://xml.spss.com/spss/viewer/viewer-style");
596   register_ns (ctx, "vtb", "http://xml.spss.com/spss/viewer/viewer-table");
597   register_ns (ctx, "vtl", "http://xml.spss.com/spss/viewer/table-looks");
598   register_ns (ctx, "vtt", "http://xml.spss.com/spss/viewer/viewer-treemodel");
599   register_ns (ctx, "vtx", "http://xml.spss.com/spss/viewer/viewer-text");
600   register_ns (ctx, "xsi", "http://www.w3.org/2001/XMLSchema-instance");
601   return ctx;
602 }
603
604 static void
605 dump_xml (int argc, char **argv, const char *member_name,
606           char *error_s, xmlDoc *doc)
607 {
608   if (!error_s)
609     {
610       if (argc == 2)
611         {
612           printf ("<!-- %s -->\n", member_name);
613           xmlElemDump (stdout, NULL, xmlDocGetRootElement (doc));
614           putchar ('\n');
615         }
616       else
617         {
618           bool any_results = false;
619
620           remove_default_xml_namespace (xmlDocGetRootElement (doc));
621           for (int i = 2; i < argc; i++)
622             {
623               xmlXPathContext *xpath_ctx = create_xpath_context (doc);
624               xmlXPathSetContextNode (xmlDocGetRootElement (doc),
625                                       xpath_ctx);
626               xmlXPathObject *xpath_obj = xmlXPathEvalExpression(
627                 CHAR_CAST (xmlChar *, argv[i]), xpath_ctx);
628               if (!xpath_obj)
629                 error (1, 0, _("%s: invalid XPath expression"), argv[i]);
630
631               const xmlNodeSet *nodes = xpath_obj->nodesetval;
632               if (nodes && nodes->nodeNr > 0)
633                 {
634                   if (!any_results)
635                     {
636                       printf ("<!-- %s -->\n", member_name);
637                       any_results = true;
638                     }
639                   for (size_t j = 0; j < nodes->nodeNr; j++)
640                     {
641                       xmlElemDump (stdout, doc, nodes->nodeTab[j]);
642                       putchar ('\n');
643                     }
644                 }
645
646               xmlXPathFreeObject (xpath_obj);
647               xmlXPathFreeContext (xpath_ctx);
648             }
649           if (any_results)
650             putchar ('\n');;
651         }
652       xmlFreeDoc (doc);
653     }
654   else
655     {
656       printf ("<!-- %s -->\n", member_name);
657       msg (ME, "%s", error_s);
658       free (error_s);
659     }
660 }
661
662 static void
663 run_dump_legacy_table (int argc, char **argv)
664 {
665   struct spv_reader *spv;
666   char *err = spv_open (argv[1], &spv);
667   if (err)
668     error (1, 0, "%s", err);
669
670   struct spv_item **items;
671   size_t n_items;
672   spv_select (spv, criteria, n_criteria, &items, &n_items);
673   for (size_t i = 0; i < n_items; i++)
674     if (spv_item_is_legacy_table (items[i]))
675       {
676         xmlDoc *doc;
677         char *error_s = spv_item_get_legacy_table (items[i], &doc);
678         dump_xml (argc, argv, items[i]->xml_member, error_s, doc);
679       }
680   free (items);
681
682   spv_close (spv);
683 }
684
685 static void
686 run_dump_structure (int argc, char **argv)
687 {
688   struct spv_reader *spv;
689   char *err = spv_open (argv[1], &spv);
690   if (err)
691     error (1, 0, "%s", err);
692
693   struct spv_item **items;
694   size_t n_items;
695   spv_select (spv, criteria, n_criteria, &items, &n_items);
696   const char *last_structure_member = NULL;
697   for (size_t i = 0; i < n_items; i++)
698     if (!last_structure_member || strcmp (items[i]->structure_member,
699                                           last_structure_member))
700       {
701         last_structure_member = items[i]->structure_member;
702
703         xmlDoc *doc;
704         char *error_s = spv_item_get_structure (items[i], &doc);
705         dump_xml (argc, argv, items[i]->structure_member, error_s, doc);
706       }
707   free (items);
708
709   spv_close (spv);
710 }
711
712 static void
713 run_is_legacy (int argc UNUSED, char **argv)
714 {
715   struct spv_reader *spv;
716   char *err = spv_open (argv[1], &spv);
717   if (err)
718     error (1, 0, "%s", err);
719
720   bool is_legacy = false;
721
722   struct spv_item **items;
723   size_t n_items;
724   spv_select (spv, criteria, n_criteria, &items, &n_items);
725   for (size_t i = 0; i < n_items; i++)
726     if (spv_item_is_legacy_table (items[i]))
727       {
728         is_legacy = true;
729         break;
730       }
731   free (items);
732
733   spv_close (spv);
734
735   exit (is_legacy ? EXIT_SUCCESS : EXIT_FAILURE);
736 }
737
738 struct command
739   {
740     const char *name;
741     int min_args, max_args;
742     void (*run) (int argc, char **argv);
743   };
744
745 static const struct command commands[] =
746   {
747     { "detect", 1, 1, run_detect },
748     { "dir", 1, 1, run_directory },
749     { "convert", 2, 2, run_convert },
750     { "get-table-look", 2, 2, run_get_table_look },
751     { "convert-table-look", 2, 2, run_convert_table_look },
752
753     /* Undocumented commands. */
754     { "dump", 1, 1, run_dump },
755     { "dump-light-table", 1, 1, run_dump_light_table },
756     { "dump-legacy-data", 1, 1, run_dump_legacy_data },
757     { "dump-legacy-table", 1, INT_MAX, run_dump_legacy_table },
758     { "dump-structure", 1, INT_MAX, run_dump_structure },
759     { "is-legacy", 1, 1, run_is_legacy },
760   };
761 static const int n_commands = sizeof commands / sizeof *commands;
762
763 static const struct command *
764 find_command (const char *name)
765 {
766   for (size_t i = 0; i < n_commands; i++)
767     {
768       const struct command *c = &commands[i];
769       if (!strcmp (name, c->name))
770         return c;
771     }
772   return NULL;
773 }
774
775 static void
776 emit_msg (const struct msg *m, void *aux UNUSED)
777 {
778   if (m->severity == MSG_S_ERROR || m->severity == MSG_S_WARNING)
779     n_warnings++;
780
781   char *s = msg_to_string (m);
782   fprintf (stderr, "%s\n", s);
783   free (s);
784 }
785
786 int
787 main (int argc, char **argv)
788 {
789   set_program_name (argv[0]);
790   msg_set_handler (emit_msg, NULL);
791   settings_init ();
792   i18n_init ();
793
794   parse_options (argc, argv);
795
796   argc -= optind;
797   argv += optind;
798
799   if (argc < 1)
800     error (1, 0, _("missing command name (use --help for help)"));
801
802   const struct command *c = find_command (argv[0]);
803   if (!c)
804     error (1, 0, _("unknown command \"%s\" (use --help for help)"), argv[0]);
805
806   int n_args = argc - 1;
807   if (n_args < c->min_args || n_args > c->max_args)
808     {
809       if (c->min_args == c->max_args)
810         {
811           error (1, 0,
812                  ngettext ("\"%s\" command takes exactly %d argument",
813                            "\"%s\" command takes exactly %d arguments",
814                            c->min_args), c->name, c->min_args);
815         }
816       else if (c->max_args == INT_MAX)
817         {
818           error (1, 0,
819                  ngettext ("\"%s\" command requires at least %d argument",
820                            "\"%s\" command requires at least %d arguments",
821                            c->min_args), c->name, c->min_args);
822         }
823       else
824         {
825           error (1, 0,
826                  _("\"%s\" command requires between %d and %d arguments"),
827                  c->name, c->min_args, c->max_args);
828         }
829     }
830
831   c->run (argc, argv);
832
833   pivot_table_look_unref (table_look);
834   i18n_done ();
835
836   return n_warnings ? EXIT_FAILURE : EXIT_SUCCESS;
837 }
838
839 static struct spv_criteria *
840 get_criteria (void)
841 {
842   if (!n_criteria || new_criteria)
843     {
844       new_criteria = false;
845       if (n_criteria >= allocated_criteria)
846         criteria = x2nrealloc (criteria, &allocated_criteria,
847                                sizeof *criteria);
848       criteria[n_criteria++] = (struct spv_criteria) SPV_CRITERIA_INITIALIZER;
849     }
850
851   return &criteria[n_criteria - 1];
852 }
853
854 static void
855 parse_select (char *arg)
856 {
857   bool invert = arg[0] == '^';
858   arg += invert;
859
860   unsigned classes = 0;
861   for (char *token = strtok (arg, ","); token; token = strtok (NULL, ","))
862     {
863       if (!strcmp (arg, "all"))
864         classes = SPV_ALL_CLASSES;
865       else if (!strcmp (arg, "help"))
866         {
867           puts (_("The following object classes are supported:"));
868           for (int class = 0; class < SPV_N_CLASSES; class++)
869             printf ("- %s\n", spv_item_class_to_string (class));
870           exit (0);
871         }
872       else
873         {
874           int class = spv_item_class_from_string (token);
875           if (class == SPV_N_CLASSES)
876             error (1, 0, _("%s: unknown object class (use --select=help "
877                            "for help"), arg);
878           classes |= 1u << class;
879         }
880     }
881
882   struct spv_criteria *c = get_criteria ();
883   c->classes = invert ? classes ^ SPV_ALL_CLASSES : classes;
884 }
885
886 static struct spv_criteria_match *
887 get_criteria_match (const char **arg)
888 {
889   struct spv_criteria *c = get_criteria ();
890   if ((*arg)[0] == '^')
891     {
892       (*arg)++;
893       return &c->exclude;
894     }
895   else
896     return &c->include;
897 }
898
899 static void
900 parse_commands (const char *arg)
901 {
902   struct spv_criteria_match *cm = get_criteria_match (&arg);
903   string_array_parse (&cm->commands, ss_cstr (arg), ss_cstr (","));
904 }
905
906 static void
907 parse_subtypes (const char *arg)
908 {
909   struct spv_criteria_match *cm = get_criteria_match (&arg);
910   string_array_parse (&cm->subtypes, ss_cstr (arg), ss_cstr (","));
911 }
912
913 static void
914 parse_labels (const char *arg)
915 {
916   struct spv_criteria_match *cm = get_criteria_match (&arg);
917   string_array_parse (&cm->labels, ss_cstr (arg), ss_cstr (","));
918 }
919
920 static void
921 parse_instances (char *arg)
922 {
923   struct spv_criteria *c = get_criteria ();
924   size_t allocated_instances = c->n_instances;
925
926   for (char *token = strtok (arg, ","); token; token = strtok (NULL, ","))
927     {
928       if (c->n_instances >= allocated_instances)
929         c->instances = x2nrealloc (c->instances, &allocated_instances,
930                                    sizeof *c->instances);
931
932       c->instances[c->n_instances++] = (!strcmp (token, "last") ? -1
933                                         : atoi (token));
934     }
935 }
936
937 static void
938 parse_nth_commands (char *arg)
939 {
940   struct spv_criteria *c = get_criteria ();
941   size_t allocated_commands = c->n_commands;
942
943   for (char *token = strtok (arg, ","); token; token = strtok (NULL, ","))
944     {
945       if (c->n_commands >= allocated_commands)
946         c->commands = x2nrealloc (c->commands, &allocated_commands,
947                                    sizeof *c->commands);
948
949       c->commands[c->n_commands++] = atoi (token);
950     }
951 }
952
953 static void
954 parse_members (const char *arg)
955 {
956   struct spv_criteria *cm = get_criteria ();
957   string_array_parse (&cm->members, ss_cstr (arg), ss_cstr (","));
958 }
959
960 static void
961 parse_table_look (const char *arg)
962 {
963   pivot_table_look_unref (table_look);
964
965   char *error_s = spv_table_look_read (arg, &table_look);
966   if (error_s)
967     error (1, 0, "%s", error_s);
968 }
969
970 static void
971 parse_options (int argc, char *argv[])
972 {
973   for (;;)
974     {
975       enum
976         {
977           OPT_MEMBER_NAMES = UCHAR_MAX + 1,
978           OPT_SHOW_HIDDEN,
979           OPT_SELECT,
980           OPT_COMMANDS,
981           OPT_NTH_COMMANDS,
982           OPT_SUBTYPES,
983           OPT_LABELS,
984           OPT_INSTANCES,
985           OPT_MEMBERS,
986           OPT_ERRORS,
987           OPT_OR,
988           OPT_SORT,
989           OPT_RAW,
990           OPT_TABLE_LOOK,
991         };
992       static const struct option long_options[] =
993         {
994           /* Input selection options. */
995           { "show-hidden", no_argument, NULL, OPT_SHOW_HIDDEN },
996           { "select", required_argument, NULL, OPT_SELECT },
997           { "commands", required_argument, NULL, OPT_COMMANDS },
998           { "nth-commands", required_argument, NULL, OPT_NTH_COMMANDS },
999           { "subtypes", required_argument, NULL, OPT_SUBTYPES },
1000           { "labels", required_argument, NULL, OPT_LABELS },
1001           { "instances", required_argument, NULL, OPT_INSTANCES },
1002           { "members", required_argument, NULL, OPT_MEMBERS },
1003           { "errors", no_argument, NULL, OPT_ERRORS },
1004           { "or", no_argument, NULL, OPT_OR },
1005
1006           /* "dir" command options. */
1007           { "member-names", no_argument, NULL, OPT_MEMBER_NAMES },
1008
1009           /* "convert" command options. */
1010           { "force", no_argument, NULL, 'f' },
1011           { "table-look", required_argument, NULL, OPT_TABLE_LOOK },
1012
1013           /* "dump-light-table" command options. */
1014           { "sort", no_argument, NULL, OPT_SORT },
1015           { "raw", no_argument, NULL, OPT_RAW },
1016
1017           { "help", no_argument, NULL, 'h' },
1018           { "version", no_argument, NULL, 'v' },
1019
1020           { NULL, 0, NULL, 0 },
1021         };
1022
1023       int c;
1024
1025       c = getopt_long (argc, argv, "O:hvf", long_options, NULL);
1026       if (c == -1)
1027         break;
1028
1029       switch (c)
1030         {
1031         case 'O':
1032           output_driver_parse_option (optarg, &output_options);
1033           break;
1034
1035         case OPT_MEMBER_NAMES:
1036           show_member_names = true;
1037           break;
1038
1039         case OPT_SHOW_HIDDEN:
1040           get_criteria ()->include_hidden = true;
1041           break;
1042
1043         case OPT_SELECT:
1044           parse_select (optarg);
1045           break;
1046
1047         case OPT_COMMANDS:
1048           parse_commands (optarg);
1049           break;
1050
1051         case OPT_NTH_COMMANDS:
1052           parse_nth_commands (optarg);
1053           break;
1054
1055         case OPT_SUBTYPES:
1056           parse_subtypes (optarg);
1057           break;
1058
1059         case OPT_LABELS:
1060           parse_labels (optarg);
1061           break;
1062
1063         case OPT_INSTANCES:
1064           parse_instances (optarg);
1065           break;
1066
1067         case OPT_MEMBERS:
1068           parse_members (optarg);
1069           break;
1070
1071         case OPT_ERRORS:
1072           get_criteria ()->error = true;
1073           break;
1074
1075         case OPT_OR:
1076           new_criteria = true;
1077           break;
1078
1079         case OPT_SORT:
1080           sort = true;
1081           break;
1082
1083         case OPT_RAW:
1084           raw = true;
1085           break;
1086
1087         case OPT_TABLE_LOOK:
1088           parse_table_look (optarg);
1089           break;
1090
1091         case 'f':
1092           force = true;
1093           break;
1094
1095         case 'v':
1096           version_etc (stdout, "pspp-output", PACKAGE_NAME, PACKAGE_VERSION,
1097                        "Ben Pfaff", "John Darrington", NULL_SENTINEL);
1098           exit (EXIT_SUCCESS);
1099
1100         case 'h':
1101           usage ();
1102           exit (EXIT_SUCCESS);
1103
1104         default:
1105           exit (EXIT_FAILURE);
1106         }
1107     }
1108 }
1109
1110 static void
1111 usage (void)
1112 {
1113   struct string s = DS_EMPTY_INITIALIZER;
1114   struct string_set formats = STRING_SET_INITIALIZER(formats);
1115   output_get_supported_formats (&formats);
1116   const char *format;
1117   const struct string_set_node *node;
1118   STRING_SET_FOR_EACH (format, node, &formats)
1119     {
1120       if (!ds_is_empty (&s))
1121         ds_put_byte (&s, ' ');
1122       ds_put_cstr (&s, format);
1123     }
1124   string_set_destroy (&formats);
1125
1126   printf ("\
1127 %s, a utility for working with SPSS viewer (.spv) files.\n\
1128 Usage: %s [OPTION]... COMMAND ARG...\n\
1129 \n\
1130 The following commands are available:\n\
1131   detect FILE            Detect whether FILE is an SPV file.\n\
1132   dir FILE               List tables and other items in FILE.\n\
1133   convert SOURCE DEST    Convert .spv SOURCE to DEST.\n\
1134   get-table-look SOURCE DEST  Copies first selected TableLook into DEST\n\
1135   convert-table-look SOURCE DEST  Copies .tlo or .stt SOURCE into DEST\n\
1136 \n\
1137 Input selection options for \"dir\" and \"convert\":\n\
1138   --select=CLASS...   include only some kinds of objects\n\
1139   --select=help       print known object classes\n\
1140   --commands=COMMAND...  include only specified COMMANDs\n\
1141   --nth-commands=N...  include only the Nth instance of selected commands\n\
1142   --subtypes=SUBTYPE...  include only specified SUBTYPEs of output\n\
1143   --labels=LABEL...   include only output objects with the given LABELs\n\
1144   --instances=INSTANCE...  include only the given object INSTANCEs\n\
1145   --show-hidden       include hidden output objects\n\
1146   --or                separate two sets of selection options\n\
1147 \n\
1148 \"convert\" by default infers the destination's format from its extension.\n\
1149 The known extensions are: %s\n\
1150 The following options override \"convert\" behavior:\n\
1151   -O format=FORMAT          set destination format to FORMAT\n\
1152   -O OPTION=VALUE           set output option\n\
1153   -f, --force               keep output file even given errors\n\
1154   --table-look=FILE         override tables' style with TableLook from FILE\n\
1155 Other options:\n\
1156   --help              display this help and exit\n\
1157   --version           output version information and exit\n",
1158           program_name, program_name, ds_cstr (&s));
1159   ds_destroy (&s);
1160 }