pivot-table: Implement hiding footnotes.
[pspp] / tests / output / pivot-table-test.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 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 <errno.h>
20 #include <fnmatch.h>
21 #include <getopt.h>
22 #include <limits.h>
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <string.h>
26
27 #include "data/file-handle-def.h"
28 #include "language/lexer/lexer.h"
29 #include "language/lexer/format-parser.h"
30 #include "libpspp/assertion.h"
31 #include "libpspp/compiler.h"
32 #include "libpspp/i18n.h"
33 #include "libpspp/string-map.h"
34 #include "output/driver.h"
35 #include "output/message-item.h"
36 #include "output/options.h"
37 #include "output/pivot-table.h"
38 #include "output/table-item.h"
39
40 #include "gl/error.h"
41 #include "gl/progname.h"
42 #include "gl/xalloc.h"
43 #include "gl/xvasprintf.h"
44
45 /* --emphasis: Enable emphasis in ASCII driver? */
46 static bool emphasis;
47
48 /* --box: ASCII driver box option. */
49 static char *box;
50
51 /* -o, --output: Base name for output files. */
52 static const char *output_base = "render";
53
54 static const char *parse_options (int argc, char **argv);
55 static void usage (void) NO_RETURN;
56 static void read_table (struct lexer *);
57 static void output_msg (const struct msg *, void *);
58
59 int
60 main (int argc, char **argv)
61 {
62   const char *input_file_name;
63
64   set_program_name (argv[0]);
65   i18n_init ();
66   output_engine_push ();
67   input_file_name = parse_options (argc, argv);
68
69   settings_init ();
70
71   struct lex_reader *reader = lex_reader_for_file (input_file_name, NULL,
72                                                    LEX_SYNTAX_AUTO,
73                                                    LEX_ERROR_CONTINUE);
74   if (!reader)
75     exit (1);
76
77   struct lexer *lexer = lex_create ();
78   msg_set_handler (output_msg, lexer);
79   lex_include (lexer, reader);
80   lex_get (lexer);
81
82   for (;;)
83     {
84       while (lex_match (lexer, T_ENDCMD))
85         continue;
86       if (lex_match (lexer, T_STOP))
87         break;
88
89       read_table (lexer);
90     }
91
92   lex_destroy (lexer);
93   output_engine_pop ();
94   fh_done ();
95
96   return 0;
97 }
98
99 static void PRINTF_FORMAT (2, 3)
100 register_driver (struct string_map *options,
101                  const char *output_file, ...)
102 {
103   va_list args;
104   va_start (args, output_file);
105   string_map_insert_nocopy (options, xstrdup ("output-file"),
106                             xvasprintf (output_file, args));
107   va_end (args);
108
109   struct output_driver *driver = output_driver_create (options);
110   if (driver == NULL)
111     exit (EXIT_FAILURE);
112   output_driver_register (driver);
113 }
114
115 static void
116 configure_drivers (int width, int length UNUSED, int min_break)
117 {
118   /* Render to stdout. */
119   struct string_map options = STRING_MAP_INITIALIZER (options);
120   string_map_insert (&options, "format", "txt");
121   string_map_insert_nocopy (&options, xstrdup ("width"),
122                             xasprintf ("%d", width));
123   if (min_break >= 0)
124     string_map_insert_nocopy (&options, xstrdup ("min-hbreak"),
125                               xasprintf ("%d", min_break));
126   string_map_insert (&options, "emphasis", emphasis ? "true" : "false");
127   if (box != NULL)
128     string_map_insert (&options, "box", box);
129   register_driver (&options, "-");
130
131
132   /* Render to <base>.pdf. */
133   string_map_insert (&options, "top-margin", "0");
134   string_map_insert (&options, "bottom-margin", "0");
135   string_map_insert (&options, "left-margin", "0");
136   string_map_insert (&options, "right-margin", "0");
137   string_map_insert (&options, "paper-size", "99x99in");
138   string_map_insert (&options, "trim", "true");
139   register_driver (&options, "%s.pdf", output_base);
140
141   string_map_insert (&options, "box", "unicode");
142   register_driver (&options, "%s.txt", output_base);
143
144   string_map_insert (&options, "box", "ascii");
145   register_driver (&options, "%s-ascii.txt", output_base);
146
147   register_driver (&options, "%s.csv", output_base);
148   register_driver (&options, "%s.odt", output_base);
149   register_driver (&options, "%s.spv", output_base);
150   register_driver (&options, "%s.html", output_base);
151   register_driver (&options, "%s.tex", output_base);
152
153   string_map_destroy (&options);
154 }
155
156 static const char *
157 parse_options (int argc, char **argv)
158 {
159   int width = 79;
160   int length = 66;
161   int min_break = -1;
162
163   for (;;)
164     {
165       enum {
166         OPT_WIDTH = UCHAR_MAX + 1,
167         OPT_LENGTH,
168         OPT_MIN_BREAK,
169         OPT_EMPHASIS,
170         OPT_BOX,
171         OPT_TABLE_LOOK,
172         OPT_HELP
173       };
174       static const struct option options[] =
175         {
176           {"width", required_argument, NULL, OPT_WIDTH},
177           {"length", required_argument, NULL, OPT_LENGTH},
178           {"min-break", required_argument, NULL, OPT_MIN_BREAK},
179           {"emphasis", no_argument, NULL, OPT_EMPHASIS},
180           {"box", required_argument, NULL, OPT_BOX},
181           {"output", required_argument, NULL, 'o'},
182           {"table-look", required_argument, NULL, OPT_TABLE_LOOK},
183           {"help", no_argument, NULL, OPT_HELP},
184           {NULL, 0, NULL, 0},
185         };
186
187       int c = getopt_long (argc, argv, "o:", options, NULL);
188       if (c == -1)
189         break;
190
191       switch (c)
192         {
193         case OPT_WIDTH:
194           width = atoi (optarg);
195           break;
196
197         case OPT_LENGTH:
198           length = atoi (optarg);
199           break;
200
201         case OPT_MIN_BREAK:
202           min_break = atoi (optarg);
203           break;
204
205         case OPT_EMPHASIS:
206           emphasis = true;
207           break;
208
209         case OPT_BOX:
210           box = optarg;
211           break;
212
213         case 'o':
214           output_base = optarg;
215           break;
216
217         case OPT_TABLE_LOOK:
218           {
219             struct pivot_table_look *look;
220             char *err = pivot_table_look_read (optarg, &look);
221             if (err)
222               error (1, 0, "%s", err);
223             pivot_table_look_set_default (look);
224             pivot_table_look_unref (look);
225           }
226           break;
227
228         case OPT_HELP:
229           usage ();
230
231         case 0:
232           break;
233
234         case '?':
235           exit (EXIT_FAILURE);
236           break;
237
238         default:
239           NOT_REACHED ();
240         }
241
242     }
243
244   configure_drivers (width, length, min_break);
245
246   if (optind + 1 != argc)
247     error (1, 0, "exactly one non-option argument required; "
248            "use --help for help");
249   return argv[optind];
250 }
251
252 static void
253 usage (void)
254 {
255   printf ("%s, to test rendering of PSPP tables\n"
256           "usage: %s [OPTIONS] INPUT\n"
257           "\nOptions:\n"
258           "  --width=WIDTH   set page width in characters\n"
259           "  --length=LINE   set page length in lines\n",
260           program_name, program_name);
261   exit (EXIT_SUCCESS);
262 }
263
264 static void
265 force_match (struct lexer *lexer, enum token_type type)
266 {
267   if (!lex_force_match (lexer, type))
268     exit (1);
269 }
270
271 static void
272 force_string (struct lexer *lexer)
273 {
274   if (!lex_force_string (lexer))
275     exit (1);
276 }
277
278 static void
279 force_int (struct lexer *lexer)
280 {
281   if (!lex_force_int (lexer))
282     exit (1);
283 }
284
285 static void
286 force_num (struct lexer *lexer)
287 {
288   if (!lex_force_num (lexer))
289     exit (1);
290 }
291
292 static bool
293 parse_settings_value_show (struct lexer *lexer, const char *name,
294                            enum settings_value_show *show)
295 {
296   if (lex_match_id (lexer, name))
297     {
298       lex_match (lexer, T_EQUALS);
299
300       if (lex_match_id (lexer, "DEFAULT"))
301         *show = SETTINGS_VALUE_SHOW_DEFAULT;
302       else if (lex_match_id (lexer, "VALUE"))
303         *show = SETTINGS_VALUE_SHOW_VALUE;
304       else if (lex_match_id (lexer, "LABEL"))
305         *show = SETTINGS_VALUE_SHOW_LABEL;
306       else if (lex_match_id (lexer, "BOTH"))
307         *show = SETTINGS_VALUE_SHOW_BOTH;
308       else
309         {
310           lex_error_expecting (lexer, "DEFAULT", "VALUE", "LABEL", "BOTH");
311           exit (1);
312         }
313
314       return true;
315     }
316   else
317     return false;
318 }
319
320 static bool
321 parse_string_setting (struct lexer *lexer, const char *name, char **stringp)
322 {
323   if (lex_match_id (lexer, name))
324     {
325       lex_match (lexer, T_EQUALS);
326       force_string (lexer);
327
328       free (*stringp);
329       *stringp = xstrdup (lex_tokcstr (lexer));
330
331       lex_get (lexer);
332       return true;
333     }
334   else
335     return false;
336 }
337
338 static bool
339 match_kw (struct lexer *lexer, const char *kw)
340 {
341   return (!strcmp (kw, "ALL")
342           ? lex_match (lexer, T_ALL)
343           : lex_match_id (lexer, kw));
344 }
345
346 static bool
347 parse_bool_setting_with_default (struct lexer *lexer, const char *name,
348                                  const char *true_kw, const char *false_kw,
349                                  int default_value, bool *out)
350 {
351   if (lex_match_id (lexer, name))
352     {
353       if (default_value >= 0)
354         {
355           if (!lex_match (lexer, T_EQUALS))
356             *out = default_value;
357           return true;
358         }
359       else
360         force_match (lexer, T_EQUALS);
361
362       if (match_kw (lexer, true_kw))
363         *out = true;
364       else if (match_kw (lexer, false_kw))
365         *out = false;
366       else
367         {
368           lex_error_expecting (lexer, true_kw, false_kw);
369           exit (1);
370         }
371
372       return true;
373     }
374   else
375     return false;
376 }
377
378 static bool
379 parse_bool_setting (struct lexer *lexer, const char *name,
380                     const char *true_kw, const char *false_kw,
381                     bool *out)
382 {
383   return parse_bool_setting_with_default (lexer, name, true_kw, false_kw, -1,
384                                           out);
385 }
386
387 static bool
388 parse_yesno_setting (struct lexer *lexer, const char *name, bool *out)
389 {
390   return parse_bool_setting_with_default (lexer, name, "YES", "NO", true, out);
391 }
392
393 static struct cell_color
394 read_color (struct lexer *lexer)
395 {
396   struct cell_color color;
397   if (!parse_color__ (lex_tokcstr (lexer), &color))
398     {
399       msg (SE, "%s: unknown color", lex_tokcstr (lexer));
400       exit (1);
401     }
402   lex_get (lexer);
403   return color;
404 }
405
406 static bool
407 parse_color_pair_setting (struct lexer *lexer, const char *name,
408                           struct cell_color out[2])
409 {
410   if (lex_match_id (lexer, name))
411     {
412       lex_match (lexer, T_EQUALS);
413       out[0] = read_color (lexer);
414       out[1] = lex_is_string (lexer) ? read_color (lexer) : out[0];
415       return true;
416     }
417   else
418     return false;
419 }
420
421 static bool
422 parse_int_setting (struct lexer *lexer, const char *name, int *out)
423 {
424   if (lex_match_id (lexer, name))
425     {
426       lex_match (lexer, T_EQUALS);
427       force_int (lexer);
428       *out = lex_integer (lexer);
429       lex_get (lexer);
430       return true;
431     }
432   else
433     return false;
434 }
435
436 static void
437 read_font_style (struct lexer *lexer, struct font_style *fs)
438 {
439   while (parse_yesno_setting (lexer, "BOLD", &fs->bold)
440          || parse_yesno_setting (lexer, "ITALIC", &fs->italic)
441          || parse_yesno_setting (lexer, "UNDERLINE", &fs->underline)
442          || parse_yesno_setting (lexer, "MARKUP", &fs->markup)
443          || parse_color_pair_setting (lexer, "FG", fs->fg)
444          || parse_color_pair_setting (lexer, "BG", fs->bg)
445          || parse_string_setting (lexer, "FACE", &fs->typeface)
446          || parse_int_setting (lexer, "SIZE", &fs->size))
447     continue;
448 }
449
450 static bool
451 parse_halign_setting (struct lexer *lexer, enum table_halign *halign,
452                       double *decimal_offset)
453 {
454   if (lex_match_id (lexer, "RIGHT"))
455     *halign = TABLE_HALIGN_RIGHT;
456   else if (lex_match_id (lexer, "LEFT"))
457     *halign = TABLE_HALIGN_LEFT;
458   else if (lex_match_id (lexer, "CELL"))
459     *halign = TABLE_HALIGN_CENTER;
460   else if (lex_match_id (lexer, "MIXED"))
461     *halign = TABLE_HALIGN_MIXED;
462   else if (lex_match_id (lexer, "DECIMAL"))
463     {
464       if (lex_is_number (lexer))
465         {
466           *decimal_offset = lex_number (lexer);
467           lex_get (lexer);
468         }
469     }
470   else
471     return false;
472
473   return true;
474 }
475
476 static bool
477 parse_valign_setting (struct lexer *lexer, enum table_valign *valign)
478 {
479   if (lex_match_id (lexer, "TOP"))
480     *valign = TABLE_VALIGN_TOP;
481   else if (lex_match_id (lexer, "MIDDLE"))
482     *valign = TABLE_VALIGN_CENTER;
483   else if (lex_match_id (lexer, "BOTTOM"))
484     *valign = TABLE_VALIGN_BOTTOM;
485   else
486     return false;
487
488   return true;
489 }
490
491 static bool
492 parse_margin_setting (struct lexer *lexer, int margin[TABLE_N_AXES][2])
493 {
494   if (lex_match_id (lexer, "MARGINS"))
495     {
496       int values[4];
497       int n = 0;
498
499       lex_match (lexer, T_EQUALS);
500       force_num (lexer);
501       while (lex_is_number (lexer) && n < 4)
502         {
503           values[n++] = lex_number (lexer);
504           lex_get (lexer);
505         }
506
507       if (n == 1)
508         {
509           margin[TABLE_HORZ][0] = margin[TABLE_HORZ][1] = values[0];
510           margin[TABLE_VERT][0] = margin[TABLE_VERT][1] = values[0];
511         }
512       else if (n == 2)
513         {
514           margin[TABLE_HORZ][0] = margin[TABLE_HORZ][1] = values[1];
515           margin[TABLE_VERT][0] = margin[TABLE_VERT][1] = values[0];
516         }
517       else if (n == 3)
518         {
519           margin[TABLE_VERT][0] = values[0];
520           margin[TABLE_HORZ][0] = margin[TABLE_HORZ][1] = values[1];
521           margin[TABLE_VERT][1] = values[2];
522         }
523       else
524         {
525           assert (n == 4);
526           margin[TABLE_VERT][0] = values[0];
527           margin[TABLE_HORZ][1] = values[1];
528           margin[TABLE_VERT][1] = values[2];
529           margin[TABLE_HORZ][0] = values[3];
530         }
531
532       return true;
533     }
534   else
535     return false;
536 }
537
538 static void
539 read_cell_style (struct lexer *lexer, struct cell_style *cs)
540 {
541   while (parse_halign_setting (lexer, &cs->halign, &cs->decimal_offset)
542          || parse_valign_setting (lexer, &cs->valign)
543          || parse_margin_setting (lexer, cs->margin))
544     continue;
545 }
546
547 static void
548 read_value_option (struct lexer *lexer, const struct pivot_table *pt,
549                    struct pivot_value *value,
550                    const struct table_area_style *base_style)
551 {
552   enum settings_value_show *show
553     = (value->type == PIVOT_VALUE_NUMERIC ? &value->numeric.show
554        : value->type == PIVOT_VALUE_STRING ? &value->string.show
555        : value->type == PIVOT_VALUE_VARIABLE ? &value->variable.show
556        : NULL);
557   if (show && parse_settings_value_show (lexer, "SHOW", show))
558     return;
559
560   char **var_name
561     = (value->type == PIVOT_VALUE_NUMERIC ? &value->numeric.var_name
562        : value->type == PIVOT_VALUE_STRING ? &value->string.var_name
563        : NULL);
564   if (var_name && parse_string_setting (lexer, "VAR", var_name))
565     return;
566
567   char **label
568     = (value->type == PIVOT_VALUE_NUMERIC ? &value->numeric.value_label
569        : value->type == PIVOT_VALUE_STRING ? &value->string.value_label
570        : value->type == PIVOT_VALUE_VARIABLE ? &value->variable.var_label
571        : NULL);
572   if (label && parse_string_setting (lexer, "LABEL", label))
573     return;
574
575   if (value->type == PIVOT_VALUE_STRING && lex_match_id (lexer, "HEX"))
576     {
577       value->string.hex = true;
578       return;
579     }
580
581   if (value->type == PIVOT_VALUE_NUMERIC)
582     {
583       msg_disable ();
584       struct fmt_spec fmt;
585       bool ok = parse_format_specifier (lexer, &fmt);
586       msg_enable ();
587
588       if (ok)
589         {
590           if (!fmt_check_output (&fmt)
591               || !fmt_check_type_compat (&fmt, VAL_NUMERIC))
592             exit (1);
593
594           value->numeric.format = fmt;
595           return;
596         }
597     }
598
599   if (lex_match_id (lexer, "SUBSCRIPTS"))
600     {
601       lex_match (lexer, T_EQUALS);
602       size_t allocated_subscripts = value->n_subscripts;
603       while (lex_token (lexer) == T_STRING)
604         {
605           if (value->n_subscripts >= allocated_subscripts)
606             value->subscripts = x2nrealloc (value->subscripts,
607                                             &allocated_subscripts,
608                                             sizeof *value->subscripts);
609
610           value->subscripts[value->n_subscripts++] = xstrdup (
611             lex_tokcstr (lexer));
612           lex_get (lexer);
613         }
614       return;
615     }
616
617   if (lex_match_id (lexer, "FONT") && base_style)
618     {
619       lex_match (lexer, T_EQUALS);
620
621       if (!value->font_style)
622         {
623           value->font_style = xmalloc (sizeof *value->font_style);
624           font_style_copy (NULL, value->font_style, &base_style->font_style);
625         }
626       read_font_style (lexer, value->font_style);
627       return;
628     }
629
630   if (lex_match_id (lexer, "CELL") && base_style)
631     {
632       lex_match (lexer, T_EQUALS);
633
634       if (!value->cell_style)
635         {
636           value->cell_style = xmalloc (sizeof *value->cell_style);
637           *value->cell_style = base_style->cell_style;
638         }
639       read_cell_style (lexer, value->cell_style);
640       return;
641     }
642
643   if (lex_match_id (lexer, "FOOTNOTE"))
644     {
645       lex_match (lexer, T_EQUALS);
646
647       while (lex_is_integer (lexer))
648         {
649           size_t idx = lex_integer (lexer);
650           lex_get (lexer);
651
652           if (idx >= pt->n_footnotes)
653             {
654               msg (SE, "Footnote %zu not available "
655                    "(only %zu footnotes defined)", idx, pt->n_footnotes);
656               exit (1);
657             }
658           pivot_value_add_footnote (value, pt->footnotes[idx]);
659         }
660       return;
661     }
662
663   lex_error (lexer, "Expecting valid value option");
664   exit (1);
665 }
666
667 static struct pivot_value *
668 read_value (struct lexer *lexer, const struct pivot_table *pt,
669             const struct table_area_style *base_style)
670 {
671   struct pivot_value *value;
672   if (lex_is_number (lexer))
673     {
674       value = pivot_value_new_number (lex_number (lexer));
675       lex_get (lexer);
676     }
677   else if (lex_is_string (lexer))
678     {
679       value = xmalloc (sizeof *value);
680       *value = (struct pivot_value) {
681         .type = PIVOT_VALUE_STRING,
682         .string = { .s = xstrdup (lex_tokcstr (lexer)) },
683       };
684       lex_get (lexer);
685     }
686   else if (lex_token (lexer) == T_ID)
687     {
688       value = xmalloc (sizeof *value);
689       *value = (struct pivot_value) {
690         .type = PIVOT_VALUE_VARIABLE,
691         .variable = { .var_name = xstrdup (lex_tokcstr (lexer)) },
692       };
693       lex_get (lexer);
694     }
695   else
696     {
697       msg (SE, "Expecting pivot_value");
698       exit (1);
699     }
700
701   while (lex_match (lexer, T_LBRACK))
702     {
703       read_value_option (lexer, pt, value, base_style);
704       force_match (lexer, T_RBRACK);
705     }
706
707   return value;
708 }
709
710 static void
711 read_group (struct lexer *lexer, struct pivot_table *pt,
712             struct pivot_category *group,
713             const struct table_area_style *label_style)
714 {
715   if (lex_match (lexer, T_ASTERISK))
716     group->show_label = true;
717
718   force_match (lexer, T_LPAREN);
719   if (lex_match (lexer, T_RPAREN))
720     return;
721
722   do
723     {
724       struct pivot_value *name = read_value (lexer, pt, label_style);
725       if (lex_token (lexer) == T_ASTERISK
726           || lex_token (lexer) == T_LPAREN)
727         read_group (lexer, pt, pivot_category_create_group__ (group, name),
728                     label_style);
729       else
730         {
731           char *rc;
732           if (lex_token (lexer) == T_ID
733               && is_pivot_result_class (lex_tokcstr (lexer)))
734             {
735               rc = xstrdup (lex_tokcstr (lexer));
736               lex_get (lexer);
737             }
738           else
739             rc = NULL;
740
741           pivot_category_create_leaf_rc (group, name, rc);
742
743           free (rc);
744         }
745     }
746   while (lex_match (lexer, T_COMMA));
747   force_match (lexer, T_RPAREN);
748 }
749
750 static void
751 read_dimension (struct lexer *lexer, struct pivot_table *pt,
752                 enum pivot_axis_type a,
753                 const struct table_area_style *label_style)
754 {
755   if (!pivot_table_is_empty (pt))
756     error (1, 0, "can't add dimensions after adding data");
757
758   lex_match (lexer, T_EQUALS);
759
760   struct pivot_value *name = read_value (lexer, pt, label_style);
761   struct pivot_dimension *dim = pivot_dimension_create__ (pt, a, name);
762   read_group (lexer, pt, dim->root, label_style);
763 }
764
765 static void
766 read_look (struct lexer *lexer, struct pivot_table *pt)
767 {
768   lex_match (lexer, T_EQUALS);
769
770   if (lex_is_string (lexer))
771     {
772       struct pivot_table_look *look;
773       char *error = pivot_table_look_read (lex_tokcstr (lexer), &look);
774       if (error)
775         {
776           msg (SE, "%s", error);
777           exit (1);
778         }
779       lex_get (lexer);
780
781       pivot_table_set_look (pt, look);
782       pivot_table_look_unref (look);
783     }
784
785   struct pivot_table_look *look = pivot_table_look_unshare (
786     pivot_table_look_ref (pt->look));
787   for (;;)
788     {
789       if (!parse_bool_setting (lexer, "EMPTY", "HIDE", "SHOW",
790                                &look->omit_empty)
791           && !parse_bool_setting (lexer, "ROWLABELS", "CORNER", "NESTED",
792                                   &look->row_labels_in_corner)
793           && !parse_bool_setting (lexer, "MARKERS", "NUMERIC", "ALPHA",
794                                   &look->show_numeric_markers)
795           && !parse_bool_setting (lexer, "LEVEL", "SUPER", "SUB",
796                                   &look->footnote_marker_superscripts)
797           && !parse_bool_setting (lexer, "LAYERS", "ALL", "CURRENT",
798                                   &look->print_all_layers)
799           && !parse_bool_setting (lexer, "PAGINATELAYERS", "YES", "NO",
800                                   &look->paginate_layers)
801           && !parse_bool_setting (lexer, "HSHRINK", "YES", "NO",
802                                   &look->shrink_to_fit[TABLE_HORZ])
803           && !parse_bool_setting (lexer, "VSHRINK", "YES", "NO",
804                                   &look->shrink_to_fit[TABLE_VERT])
805           && !parse_bool_setting (lexer, "TOPCONTINUATION", "YES", "NO",
806                                   &look->top_continuation)
807           && !parse_bool_setting (lexer, "BOTTOMCONTINUATION", "YES", "NO",
808                                   &look->bottom_continuation)
809           && !parse_string_setting (lexer, "CONTINUATION",
810                                     &look->continuation))
811         break;
812     }
813   pivot_table_set_look (pt, look);
814   pivot_table_look_unref (look);
815 }
816
817 static enum table_stroke
818 read_stroke (struct lexer *lexer)
819 {
820   for (int stroke = 0; stroke < TABLE_N_STROKES; stroke++)
821     if (lex_match_id (lexer, table_stroke_to_string (stroke)))
822       return stroke;
823
824   lex_error (lexer, "expecting stroke");
825   exit (1);
826 }
827
828 static bool
829 parse_value_setting (struct lexer *lexer, const struct pivot_table *pt,
830                      const char *name,
831                      struct pivot_value **valuep,
832                      struct table_area_style *base_style)
833 {
834   if (lex_match_id (lexer, name))
835     {
836       lex_match (lexer, T_EQUALS);
837
838       pivot_value_destroy (*valuep);
839       *valuep = read_value (lexer, pt, base_style);
840
841       return true;
842     }
843   else
844     return false;
845 }
846
847 static void
848 read_border (struct lexer *lexer, struct pivot_table *pt)
849 {
850   static const char *const pivot_border_ids[PIVOT_N_BORDERS] = {
851     [PIVOT_BORDER_TITLE] = "title",
852     [PIVOT_BORDER_OUTER_LEFT] = "outer-left",
853     [PIVOT_BORDER_OUTER_TOP] = "outer-top",
854     [PIVOT_BORDER_OUTER_RIGHT] = "outer-right",
855     [PIVOT_BORDER_OUTER_BOTTOM] = "outer-bottom",
856     [PIVOT_BORDER_INNER_LEFT] = "inner-left",
857     [PIVOT_BORDER_INNER_TOP] = "inner-top",
858     [PIVOT_BORDER_INNER_RIGHT] = "inner-right",
859     [PIVOT_BORDER_INNER_BOTTOM] = "inner-bottom",
860     [PIVOT_BORDER_DATA_LEFT] = "data-left",
861     [PIVOT_BORDER_DATA_TOP] = "data-top",
862     [PIVOT_BORDER_DIM_ROW_HORZ] = "dim-row-horz",
863     [PIVOT_BORDER_DIM_ROW_VERT] = "dim-row-vert",
864     [PIVOT_BORDER_DIM_COL_HORZ] = "dim-col-horz",
865     [PIVOT_BORDER_DIM_COL_VERT] = "dim-col-vert",
866     [PIVOT_BORDER_CAT_ROW_HORZ] = "cat-row-horz",
867     [PIVOT_BORDER_CAT_ROW_VERT] = "cat-row-vert",
868     [PIVOT_BORDER_CAT_COL_HORZ] = "cat-col-horz",
869     [PIVOT_BORDER_CAT_COL_VERT] = "cat-col-vert",
870   };
871
872   lex_match (lexer, T_EQUALS);
873
874   struct pivot_table_look *look = pivot_table_look_unshare (
875     pivot_table_look_ref (pt->look));
876   while (lex_token (lexer) == T_STRING)
877     {
878       char *s = xstrdup (lex_tokcstr (lexer));
879       lex_get (lexer);
880       force_match (lexer, T_LPAREN);
881
882       struct table_border_style style = TABLE_BORDER_STYLE_INITIALIZER;
883       style.stroke = read_stroke (lexer);
884       if (lex_is_string (lexer))
885         style.color = read_color (lexer);
886       force_match (lexer, T_RPAREN);
887
888       int n = 0;
889       for (int b = 0; b < PIVOT_N_BORDERS; b++)
890         {
891           if (!fnmatch (s, pivot_border_ids[b], 0))
892             {
893               look->borders[b] = style;
894               n++;
895             }
896         }
897       if (!n)
898         {
899           msg (SE, "%s: no matching borders", s);
900           exit (1);
901         }
902       free (s);
903     }
904   pivot_table_set_look (pt, look);
905   pivot_table_look_unref (look);
906 }
907
908 static void
909 read_footnote (struct lexer *lexer, struct pivot_table *pt)
910 {
911   size_t idx;
912   if (lex_match (lexer, T_LBRACK))
913     {
914       force_int (lexer);
915
916       idx = lex_integer (lexer);
917       lex_get (lexer);
918
919       force_match (lexer, T_RBRACK);
920     }
921   else
922     idx = pt->n_footnotes;
923   lex_match (lexer, T_EQUALS);
924
925   struct pivot_value *content
926     = read_value (lexer, pt, &pt->look->areas[PIVOT_AREA_FOOTER]);
927
928   struct pivot_value *marker;
929   if (lex_match_id (lexer, "MARKER"))
930     {
931       lex_match (lexer, T_EQUALS);
932       marker = read_value (lexer, pt, &pt->look->areas[PIVOT_AREA_FOOTER]);
933     }
934   else
935     marker = NULL;
936
937   bool show = !lex_match_id (lexer, "HIDE");
938   pivot_table_create_footnote__ (pt, idx, marker, content)->show = show;
939 }
940
941 static void
942 read_cell (struct lexer *lexer, struct pivot_table *pt)
943 {
944   force_match (lexer, T_LBRACK);
945
946   size_t *lo = xnmalloc (pt->n_dimensions, sizeof *lo);
947   size_t *hi = xnmalloc (pt->n_dimensions, sizeof *hi);
948   for (size_t i = 0; i < pt->n_dimensions; i++)
949     {
950       const struct pivot_dimension *d = pt->dimensions[i];
951
952       if (i)
953         force_match (lexer, T_COMMA);
954
955       if (!d->n_leaves)
956         {
957           msg (SE, "can't define data because dimension %zu has no categories",
958                i);
959           exit (1);
960         }
961
962       if (lex_match (lexer, T_ALL))
963         {
964           lo[i] = 0;
965           hi[i] = d->n_leaves - 1;
966         }
967       else
968         {
969           force_int (lexer);
970           lo[i] = hi[i] = lex_integer (lexer);
971           lex_get (lexer);
972
973           if (lex_match_id (lexer, "THRU"))
974             {
975               force_int (lexer);
976               hi[i] = lex_integer (lexer);
977               lex_get (lexer);
978             }
979
980           if (hi[i] < lo[i])
981             {
982               msg (SE, "%zu THRU %zu is not a valid range", lo[i], hi[i]);
983               exit (1);
984             }
985           if (hi[i] >= d->n_leaves)
986             {
987               msg (SE, "dimension %zu (%s) has only %zu categories",
988                    i, pivot_value_to_string (d->root->name, pt),
989                    d->n_leaves);
990               exit (1);
991             }
992         }
993     }
994   force_match (lexer, T_RBRACK);
995
996   struct pivot_value *value = NULL;
997   bool delete = false;
998   if (lex_match (lexer, T_EQUALS))
999     {
1000       if (lex_match_id (lexer, "DELETE"))
1001         delete = true;
1002       else
1003         value = read_value (lexer, pt, &pt->look->areas[PIVOT_AREA_DATA]);
1004     }
1005
1006   size_t *dindexes = xmemdup (lo, pt->n_dimensions * sizeof *lo);
1007   for (size_t i = 0; ; i++)
1008     {
1009       if (delete)
1010         pivot_table_delete (pt, dindexes);
1011       else
1012         pivot_table_put (pt, dindexes, pt->n_dimensions,
1013                          (value
1014                           ? pivot_value_clone (value)
1015                           : pivot_value_new_integer (i)));
1016
1017       for (size_t j = 0; j < pt->n_dimensions; j++)
1018         {
1019           if (++dindexes[j] <= hi[j])
1020             goto next;
1021           dindexes[j] = lo[j];
1022         }
1023         break;
1024     next:;
1025     }
1026   free (dindexes);
1027
1028   pivot_value_destroy (value);
1029
1030   free (lo);
1031   free (hi);
1032 }
1033
1034 static struct pivot_dimension *
1035 parse_dim_name (struct lexer *lexer, struct pivot_table *table)
1036 {
1037   force_string (lexer);
1038   for (size_t i = 0; i < table->n_dimensions; i++)
1039     {
1040       struct pivot_dimension *dim = table->dimensions[i];
1041
1042       struct string s = DS_EMPTY_INITIALIZER;
1043       pivot_value_format_body (dim->root->name, table, &s);
1044       bool match = !strcmp (ds_cstr (&s), lex_tokcstr (lexer));
1045       ds_destroy (&s);
1046
1047       if (match)
1048         {
1049           lex_get (lexer);
1050           return dim;
1051         }
1052     }
1053
1054   lex_error (lexer, "unknown dimension");
1055   exit (1);
1056 }
1057
1058 static enum pivot_axis_type
1059 parse_axis_type (struct lexer *lexer)
1060 {
1061   if (lex_match_id (lexer, "ROW"))
1062     return PIVOT_AXIS_ROW;
1063   else if (lex_match_id (lexer, "COLUMN"))
1064     return PIVOT_AXIS_COLUMN;
1065   else if (lex_match_id (lexer, "LAYER"))
1066     return PIVOT_AXIS_LAYER;
1067   else
1068     {
1069       lex_error_expecting (lexer, "ROW", "COLUMN", "LAYER");
1070       exit (1);
1071     }
1072 }
1073
1074 static void
1075 move_dimension (struct lexer *lexer, struct pivot_table *table)
1076 {
1077   struct pivot_dimension *dim = parse_dim_name (lexer, table);
1078
1079   enum pivot_axis_type axis = parse_axis_type (lexer);
1080
1081   size_t position;
1082   if (lex_is_integer (lexer))
1083     {
1084       position = lex_integer (lexer);
1085       lex_get (lexer);
1086     }
1087   else
1088     position = 0;
1089
1090   pivot_table_move_dimension (table, dim, axis, position);
1091 }
1092
1093 static void
1094 swap_axes (struct lexer *lexer, struct pivot_table *table)
1095 {
1096   enum pivot_axis_type a = parse_axis_type (lexer);
1097   enum pivot_axis_type b = parse_axis_type (lexer);
1098   pivot_table_swap_axes (table, a, b);
1099 }
1100
1101 static void
1102 read_current_layer (struct lexer *lexer, struct pivot_table *table)
1103 {
1104   lex_match (lexer, T_EQUALS);
1105
1106   const struct pivot_axis *layer_axis = &table->axes[PIVOT_AXIS_LAYER];
1107   for (size_t i = 0; i < layer_axis->n_dimensions; i++)
1108     {
1109       const struct pivot_dimension *dim = layer_axis->dimensions[i];
1110
1111       force_int (lexer);
1112       size_t index = lex_integer (lexer);
1113       if (index >= dim->n_leaves)
1114         {
1115           lex_error (lexer, "only %zu dimensions", dim->n_leaves);
1116           exit (1);
1117         }
1118       lex_get (lexer);
1119
1120       table->current_layer[i] = index;
1121     }
1122 }
1123
1124 static void
1125 read_table (struct lexer *lexer)
1126 {
1127   bool displayed = false;
1128
1129   struct pivot_table *pt = pivot_table_create ("Default Title");
1130   while (lex_match (lexer, T_SLASH))
1131     {
1132       assert (!pivot_table_is_shared (pt));
1133       displayed = false;
1134
1135       if (lex_match_id (lexer, "ROW"))
1136         read_dimension (lexer, pt, PIVOT_AXIS_ROW,
1137                         &pt->look->areas[PIVOT_AREA_ROW_LABELS]);
1138       else if (lex_match_id (lexer, "COLUMN"))
1139         read_dimension (lexer, pt, PIVOT_AXIS_COLUMN,
1140                         &pt->look->areas[PIVOT_AREA_COLUMN_LABELS]);
1141       else if (lex_match_id (lexer, "LAYER"))
1142         read_dimension (lexer, pt, PIVOT_AXIS_LAYER,
1143                         &pt->look->areas[PIVOT_AREA_LAYERS]);
1144       else if (lex_match_id (lexer, "LOOK"))
1145         read_look (lexer, pt);
1146       else if (lex_match_id (lexer, "ROTATE"))
1147         {
1148           lex_match (lexer, T_EQUALS);
1149           while (lex_token (lexer) == T_ID)
1150             if (!parse_bool_setting (lexer, "INNERCOLUMNS", "YES", "NO",
1151                                      &pt->rotate_inner_column_labels)
1152                 && !parse_bool_setting (lexer, "OUTERROWS", "YES", "NO",
1153                                         &pt->rotate_outer_row_labels))
1154               break;
1155         }
1156       else if (lex_match_id (lexer, "SHOW"))
1157         {
1158           lex_match (lexer, T_EQUALS);
1159           while (lex_token (lexer) == T_ID)
1160             {
1161               if (parse_bool_setting (lexer, "GRID", "YES", "NO",
1162                                       &pt->show_grid_lines)
1163                   || parse_bool_setting (lexer, "CAPTION", "YES", "NO",
1164                                          &pt->show_caption)
1165                   || parse_bool_setting (lexer, "TITLE", "YES", "NO",
1166                                          &pt->show_title))
1167                 continue;
1168
1169               if (parse_settings_value_show (lexer, "VALUES", &pt->show_values)
1170                   || parse_settings_value_show (lexer, "VARIABLES",
1171                                                 &pt->show_variables))
1172                 continue;
1173
1174               if (lex_match_id (lexer, "LAYER"))
1175                 read_current_layer (lexer, pt);
1176
1177               break;
1178             }
1179         }
1180       else if (parse_value_setting (lexer, pt, "TITLE", &pt->title,
1181                                     &pt->look->areas[PIVOT_AREA_TITLE])
1182                || parse_value_setting (lexer, pt, "SUBTYPE", &pt->subtype,
1183                                        NULL)
1184                || parse_value_setting (lexer, pt, "CORNER", &pt->corner_text,
1185                                        &pt->look->areas[PIVOT_AREA_CORNER])
1186                || parse_value_setting (lexer, pt, "CAPTION", &pt->caption,
1187                                        &pt->look->areas[PIVOT_AREA_CAPTION])
1188                || parse_string_setting (lexer, "NOTES", &pt->notes))
1189         {
1190           /* Nothing. */
1191         }
1192       else if (lex_match_id (lexer, "BORDER"))
1193         read_border (lexer, pt);
1194       else if (lex_match_id (lexer, "TRANSPOSE"))
1195         pivot_table_transpose (pt);
1196       else if (lex_match_id (lexer, "SWAP"))
1197         swap_axes (lexer, pt);
1198       else if (lex_match_id (lexer, "MOVE"))
1199         move_dimension (lexer, pt);
1200       else if (lex_match_id (lexer, "CELLS"))
1201         read_cell (lexer, pt);
1202       else if (lex_match_id (lexer, "FOOTNOTE"))
1203         read_footnote (lexer, pt);
1204       else if (lex_match_id (lexer, "DUMP"))
1205         pivot_table_dump (pt, 0);
1206       else if (lex_match_id (lexer, "DISPLAY"))
1207         {
1208           pivot_table_submit (pivot_table_ref (pt));
1209           pt = pivot_table_unshare (pt);
1210           displayed = true;
1211         }
1212       else
1213         {
1214           msg (SE, "Expecting keyword");
1215           exit (1);
1216         }
1217     }
1218
1219   if (!displayed)
1220     pivot_table_submit (pt);
1221   else
1222     pivot_table_unshare (pt);
1223
1224   force_match (lexer, T_ENDCMD);
1225 }
1226
1227 static void
1228 output_msg (const struct msg *m_, void *lexer_)
1229 {
1230   struct lexer *lexer = lexer_;
1231   struct msg m = *m_;
1232
1233   if (m.file_name == NULL)
1234     {
1235       m.file_name = CONST_CAST (char *, lex_get_file_name (lexer));
1236       m.first_line = lex_get_first_line_number (lexer, 0);
1237       m.last_line = lex_get_last_line_number (lexer, 0);
1238     }
1239
1240   m.command_name = output_get_command_name ();
1241
1242   message_item_submit (message_item_create (&m));
1243
1244   free (m.command_name);
1245 }