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