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