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