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