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