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