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