work
[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 <getopt.h>
21 #include <limits.h>
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <string.h>
25
26 #include "data/file-handle-def.h"
27 #include "language/lexer/lexer.h"
28 #include "libpspp/assertion.h"
29 #include "libpspp/compiler.h"
30 #include "libpspp/i18n.h"
31 #include "libpspp/string-map.h"
32 #include "output/driver.h"
33 #include "output/pivot-table.h"
34 #include "output/table-item.h"
35
36 #include "gl/error.h"
37 #include "gl/progname.h"
38 #include "gl/xalloc.h"
39 #include "gl/xvasprintf.h"
40
41 /* --emphasis: Enable emphasis in ASCII driver? */
42 static bool emphasis;
43
44 /* --box: ASCII driver box option. */
45 static char *box;
46
47 /* -o, --output: Base name for output files. */
48 static const char *output_base = "render";
49
50 static const char *parse_options (int argc, char **argv);
51 static void usage (void) NO_RETURN;
52 static struct pivot_table *read_table (struct lexer *);
53
54 int
55 main (int argc, char **argv)
56 {
57   const char *input_file_name;
58
59   set_program_name (argv[0]);
60   i18n_init ();
61   output_engine_push ();
62   input_file_name = parse_options (argc, argv);
63
64   settings_init ();
65
66   struct lex_reader *reader = lex_reader_for_file (input_file_name, NULL,
67                                                    LEX_SYNTAX_AUTO,
68                                                    LEX_ERROR_CONTINUE);
69   if (!reader)
70     exit (1);
71
72   struct lexer *lexer = lex_create ();
73   lex_include (lexer, reader);
74   lex_get (lexer);
75
76   for (;;)
77     {
78       while (lex_match (lexer, T_ENDCMD))
79         continue;
80       if (lex_match (lexer, T_STOP))
81         break;
82
83       struct pivot_table *pt = read_table (lexer);
84       pivot_table_dump (pt, 0);
85       pivot_table_submit (pt);
86     }
87
88   lex_destroy (lexer);
89   output_engine_pop ();
90   fh_done ();
91
92   return 0;
93 }
94
95 static void PRINTF_FORMAT (2, 3)
96 register_driver (struct string_map *options,
97                  const char *output_file, ...)
98 {
99   va_list args;
100   va_start (args, output_file);
101   string_map_insert_nocopy (options, xstrdup ("output-file"),
102                             xvasprintf (output_file, args));
103   va_end (args);
104
105   struct output_driver *driver = output_driver_create (options);
106   if (driver == NULL)
107     exit (EXIT_FAILURE);
108   output_driver_register (driver);
109 }
110
111 static void
112 configure_drivers (int width, int length, int min_break)
113 {
114   /* Render to stdout. */
115   struct string_map options = STRING_MAP_INITIALIZER (options);
116   string_map_insert (&options, "format", "txt");
117   string_map_insert_nocopy (&options, xstrdup ("width"),
118                             xasprintf ("%d", width));
119   if (min_break >= 0)
120     string_map_insert_nocopy (&options, xstrdup ("min-hbreak"),
121                               xasprintf ("%d", min_break));
122   string_map_insert (&options, "emphasis", emphasis ? "true" : "false");
123   if (box != NULL)
124     string_map_insert (&options, "box", box);
125   register_driver (&options, "-");
126
127   /* Render to <base>.txt. */
128   register_driver (&options, "%s.txt", output_base);
129
130 #ifdef HAVE_CAIRO
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_nocopy (&options, xstrdup ("paper-size"),
137                             xasprintf ("%dx%dpt", width * 5, length * 8));
138   if (min_break >= 0)
139     {
140       string_map_insert_nocopy (&options, xstrdup ("min-hbreak"),
141                                 xasprintf ("%d", min_break * 5));
142       string_map_insert_nocopy (&options, xstrdup ("min-vbreak"),
143                                 xasprintf ("%d", min_break * 8));
144     }
145   register_driver (&options, "%s.pdf", output_base);
146 #endif
147
148   /* Render to <base>.csv. */
149   register_driver (&options, "%s.csv", output_base);
150
151   /* Render to <base>.odt. */
152   register_driver (&options, "%s.odt", output_base);
153
154   string_map_destroy (&options);
155 }
156
157 static const char *
158 parse_options (int argc, char **argv)
159 {
160   int width = 79;
161   int length = 66;
162   int min_break = -1;
163
164   for (;;)
165     {
166       enum {
167         OPT_WIDTH = UCHAR_MAX + 1,
168         OPT_LENGTH,
169         OPT_MIN_BREAK,
170         OPT_EMPHASIS,
171         OPT_BOX,
172         OPT_HELP
173       };
174       static const struct option options[] =
175         {
176           {"width", required_argument, NULL, OPT_WIDTH},
177           {"length", required_argument, NULL, OPT_LENGTH},
178           {"min-break", required_argument, NULL, OPT_MIN_BREAK},
179           {"emphasis", no_argument, NULL, OPT_EMPHASIS},
180           {"box", required_argument, NULL, OPT_BOX},
181           {"output", required_argument, NULL, 'o'},
182           {"help", no_argument, NULL, OPT_HELP},
183           {NULL, 0, NULL, 0},
184         };
185
186       int c = getopt_long (argc, argv, "o:", options, NULL);
187       if (c == -1)
188         break;
189
190       switch (c)
191         {
192         case OPT_WIDTH:
193           width = atoi (optarg);
194           break;
195
196         case OPT_LENGTH:
197           length = atoi (optarg);
198           break;
199
200         case OPT_MIN_BREAK:
201           min_break = atoi (optarg);
202           break;
203
204         case OPT_EMPHASIS:
205           emphasis = true;
206           break;
207
208         case OPT_BOX:
209           box = optarg;
210           break;
211
212         case 'o':
213           output_base = optarg;
214           break;
215
216         case OPT_HELP:
217           usage ();
218
219         case 0:
220           break;
221
222         case '?':
223           exit(EXIT_FAILURE);
224           break;
225
226         default:
227           NOT_REACHED ();
228         }
229
230     }
231
232   configure_drivers (width, length, min_break);
233
234   if (optind + 1 != argc)
235     error (1, 0, "exactly one non-option argument required; "
236            "use --help for help");
237   return argv[optind];
238 }
239
240 static void
241 usage (void)
242 {
243   printf ("%s, to test rendering of PSPP tables\n"
244           "usage: %s [OPTIONS] INPUT\n"
245           "\nOptions:\n"
246           "  --width=WIDTH   set page width in characters\n"
247           "  --length=LINE   set page length in lines\n",
248           program_name, program_name);
249   exit (EXIT_SUCCESS);
250 }
251
252 static struct pivot_value *
253 read_value (struct lexer *lexer)
254 {
255   struct pivot_value *value;
256   if (lex_is_number (lexer))
257     {
258       value = pivot_value_new_number (lex_number (lexer));
259       lex_get (lexer);
260     }
261   else if (lex_is_string (lexer))
262     {
263       value = pivot_value_new_user_text (lex_tokcstr (lexer), SIZE_MAX);
264       lex_get (lexer);
265     }
266   else
267     {
268       msg (SE, "Expecting pivot_value");
269       exit (1);
270     }
271
272   if (!lex_match (lexer, T_LBRACK))
273     return value;
274   do
275     {
276       msg (SE, "Options not yet supported");
277       exit (1);
278     }
279   while (lex_match (lexer, T_COMMA));
280   if (!lex_force_match (lexer, T_RBRACK))
281     exit (1);
282 }
283
284 static void
285 read_group (struct lexer *lexer, struct pivot_table *pt,
286             struct pivot_category *group)
287 {
288   if (lex_match (lexer, T_ASTERISK))
289     group->show_label = true;
290
291   if (!lex_force_match (lexer, T_LPAREN))
292     exit (1);
293
294   if (lex_match (lexer, T_RPAREN))
295     return;
296
297   do
298     {
299       struct pivot_value *name = read_value (lexer);
300       if (lex_token (lexer) == T_ASTERISK
301           || lex_token (lexer) == T_LPAREN)
302         read_group (lexer, pt, pivot_category_create_group__ (group, name));
303       else
304         {
305           char *rc;
306           if (lex_token (lexer) == T_ID
307               && is_pivot_result_class (lex_tokcstr (lexer)))
308             {
309               rc = xstrdup (lex_tokcstr (lexer));
310               lex_get (lexer);
311             }
312           else
313             rc = NULL;
314
315           pivot_category_create_leaf_rc (group, name, rc);
316
317           free (rc);
318         }
319     }
320   while (lex_match (lexer, T_COMMA));
321   if (!lex_force_match (lexer, T_RPAREN))
322     exit (1);
323 }
324
325 static void
326 read_dimension (struct lexer *lexer, struct pivot_table *pt,
327                 enum pivot_axis_type a)
328 {
329   lex_match (lexer, T_EQUALS);
330
331   struct pivot_value *name = read_value (lexer);
332   struct pivot_dimension *dim = pivot_dimension_create__ (pt, a, name);
333   read_group (lexer, pt, dim->root);
334 }
335
336 static void
337 read_look (struct lexer *lexer, struct pivot_table *pt)
338 {
339   lex_match (lexer, T_EQUALS);
340
341   if (lex_is_string (lexer))
342     {
343       struct pivot_table_look *look;
344       char *error = pivot_table_look_read (lex_tokcstr (lexer), &look);
345       if (error)
346         {
347           msg (SE, "%s", error);
348           exit (1);
349         }
350
351       pivot_table_set_look (pt, look);
352       pivot_table_look_unref (look);
353     }
354 }
355
356 static struct pivot_table *
357 read_table (struct lexer *lexer)
358 {
359   struct pivot_table *pt = pivot_table_create__ (NULL, NULL);
360   while (lex_match (lexer, T_SLASH))
361     {
362       if (lex_match_id (lexer, "ROW"))
363         read_dimension (lexer, pt, PIVOT_AXIS_ROW);
364       else if (lex_match_id (lexer, "COLUMN"))
365         read_dimension (lexer, pt, PIVOT_AXIS_COLUMN);
366       else if (lex_match_id (lexer, "LAYER"))
367         read_dimension (lexer, pt, PIVOT_AXIS_LAYER);
368       else if (lex_match_id (lexer, "LOOK"))
369         read_look (lexer, pt);
370       else
371         {
372           msg (SE, "Expecting keyword");
373           exit (1);
374         }
375     }
376
377   if (!lex_force_match (lexer, T_ENDCMD))
378     exit (1);
379   return pt;
380 }