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