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