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