1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
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.
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.
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/>. */
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/message-item.h"
34 #include "output/options.h"
35 #include "output/pivot-table.h"
36 #include "output/table-item.h"
39 #include "gl/progname.h"
40 #include "gl/xalloc.h"
41 #include "gl/xvasprintf.h"
43 /* --emphasis: Enable emphasis in ASCII driver? */
46 /* --box: ASCII driver box option. */
49 /* -o, --output: Base name for output files. */
50 static const char *output_base = "render";
52 static const char *parse_options (int argc, char **argv);
53 static void usage (void) NO_RETURN;
54 static struct pivot_table *read_table (struct lexer *);
55 static void output_msg (const struct msg *, void *);
58 main (int argc, char **argv)
60 const char *input_file_name;
62 set_program_name (argv[0]);
64 output_engine_push ();
65 input_file_name = parse_options (argc, argv);
69 struct lex_reader *reader = lex_reader_for_file (input_file_name, NULL,
75 struct lexer *lexer = lex_create ();
76 msg_set_handler (output_msg, lexer);
77 lex_include (lexer, reader);
82 while (lex_match (lexer, T_ENDCMD))
84 if (lex_match (lexer, T_STOP))
87 struct pivot_table *pt = read_table (lexer);
88 pivot_table_dump (pt, 0);
89 pivot_table_submit (pt);
99 static void PRINTF_FORMAT (2, 3)
100 register_driver (struct string_map *options,
101 const char *output_file, ...)
104 va_start (args, output_file);
105 string_map_insert_nocopy (options, xstrdup ("output-file"),
106 xvasprintf (output_file, args));
109 struct output_driver *driver = output_driver_create (options);
112 output_driver_register (driver);
116 configure_drivers (int width, int length, int min_break)
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));
124 string_map_insert_nocopy (&options, xstrdup ("min-hbreak"),
125 xasprintf ("%d", min_break));
126 string_map_insert (&options, "emphasis", emphasis ? "true" : "false");
128 string_map_insert (&options, "box", box);
129 register_driver (&options, "-");
131 /* Render to <base>.txt. */
132 register_driver (&options, "%s.txt", output_base);
135 /* Render to <base>.pdf. */
136 string_map_insert (&options, "top-margin", "0");
137 string_map_insert (&options, "bottom-margin", "0");
138 string_map_insert (&options, "left-margin", "0");
139 string_map_insert (&options, "right-margin", "0");
140 string_map_insert_nocopy (&options, xstrdup ("paper-size"),
141 xasprintf ("%dx%dpt", width * 5, length * 8));
144 string_map_insert_nocopy (&options, xstrdup ("min-hbreak"),
145 xasprintf ("%d", min_break * 5));
146 string_map_insert_nocopy (&options, xstrdup ("min-vbreak"),
147 xasprintf ("%d", min_break * 8));
149 register_driver (&options, "%s.pdf", output_base);
152 /* Render to <base>.csv. */
153 register_driver (&options, "%s.csv", output_base);
155 /* Render to <base>.odt. */
156 register_driver (&options, "%s.odt", output_base);
158 string_map_destroy (&options);
162 parse_options (int argc, char **argv)
171 OPT_WIDTH = UCHAR_MAX + 1,
179 static const struct option options[] =
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},
192 int c = getopt_long (argc, argv, "o:", options, NULL);
199 width = atoi (optarg);
203 length = atoi (optarg);
207 min_break = atoi (optarg);
219 output_base = optarg;
224 struct pivot_table_look *look;
225 char *err = pivot_table_look_read (optarg, &look);
227 error (1, 0, "%s", err);
228 pivot_table_look_set_default (look);
229 pivot_table_look_unref (look);
249 configure_drivers (width, length, min_break);
251 if (optind + 1 != argc)
252 error (1, 0, "exactly one non-option argument required; "
253 "use --help for help");
260 printf ("%s, to test rendering of PSPP tables\n"
261 "usage: %s [OPTIONS] INPUT\n"
263 " --width=WIDTH set page width in characters\n"
264 " --length=LINE set page length in lines\n",
265 program_name, program_name);
269 static struct pivot_value *
270 read_value (struct lexer *lexer)
272 struct pivot_value *value;
273 if (lex_is_number (lexer))
275 value = pivot_value_new_number (lex_number (lexer));
278 else if (lex_is_string (lexer))
280 value = pivot_value_new_user_text (lex_tokcstr (lexer), SIZE_MAX);
285 msg (SE, "Expecting pivot_value");
289 if (!lex_match (lexer, T_LBRACK))
293 msg (SE, "Options not yet supported");
296 while (lex_match (lexer, T_COMMA));
297 if (!lex_force_match (lexer, T_RBRACK))
302 read_group (struct lexer *lexer, struct pivot_table *pt,
303 struct pivot_category *group)
305 if (lex_match (lexer, T_ASTERISK))
306 group->show_label = true;
308 if (!lex_force_match (lexer, T_LPAREN))
311 if (lex_match (lexer, T_RPAREN))
316 struct pivot_value *name = read_value (lexer);
317 if (lex_token (lexer) == T_ASTERISK
318 || lex_token (lexer) == T_LPAREN)
319 read_group (lexer, pt, pivot_category_create_group__ (group, name));
323 if (lex_token (lexer) == T_ID
324 && is_pivot_result_class (lex_tokcstr (lexer)))
326 rc = xstrdup (lex_tokcstr (lexer));
332 pivot_category_create_leaf_rc (group, name, rc);
337 while (lex_match (lexer, T_COMMA));
338 if (!lex_force_match (lexer, T_RPAREN))
343 read_dimension (struct lexer *lexer, struct pivot_table *pt,
344 enum pivot_axis_type a)
346 lex_match (lexer, T_EQUALS);
348 struct pivot_value *name = read_value (lexer);
349 struct pivot_dimension *dim = pivot_dimension_create__ (pt, a, name);
350 read_group (lexer, pt, dim->root);
354 parse_bool_setting (struct lexer *lexer, const char *name,
355 const char *true_kw, const char *false_kw,
358 if (lex_match_id (lexer, name))
360 if (!lex_force_match (lexer, T_EQUALS))
363 if (lex_match_id (lexer, true_kw))
365 else if (lex_match_id (lexer, false_kw))
369 lex_error_expecting (lexer, true_kw, false_kw);
380 read_look (struct lexer *lexer, struct pivot_table *pt)
382 lex_match (lexer, T_EQUALS);
384 if (lex_is_string (lexer))
386 struct pivot_table_look *look;
387 char *error = pivot_table_look_read (lex_tokcstr (lexer), &look);
390 msg (SE, "%s", error);
395 pivot_table_set_look (pt, look);
396 pivot_table_look_unref (look);
399 struct pivot_table_look *look = pivot_table_look_unshare (
400 pivot_table_look_ref (pt->look));
403 if (!parse_bool_setting (lexer, "EMPTY", "HIDE", "SHOW",
405 && !parse_bool_setting (lexer, "ROWLABELS", "CORNER", "NESTED",
406 &look->row_labels_in_corner)
407 && !parse_bool_setting (lexer, "MARKERS", "NUMERIC", "ALPHA",
408 &look->show_numeric_markers)
409 && !parse_bool_setting (lexer, "LEVEL", "SUPER", "SUB",
410 &look->footnote_marker_superscripts)
411 && !parse_bool_setting (lexer, "LAYERS", "CURRENT", "ALL",
412 &look->print_all_layers)
413 && !parse_bool_setting (lexer, "PAGINATELAYERS", "YES", "NO",
414 &look->paginate_layers)
415 && !parse_bool_setting (lexer, "HSHRINK", "YES", "NO",
416 &look->shrink_to_fit[TABLE_HORZ])
417 && !parse_bool_setting (lexer, "VSHRINK", "YES", "NO",
418 &look->shrink_to_fit[TABLE_VERT])
419 && !parse_bool_setting (lexer, "TOPCONTINUATION", "YES", "NO",
420 &look->top_continuation)
421 && !parse_bool_setting (lexer, "BOTTOMCONTINUATION", "YES", "NO",
422 &look->bottom_continuation))
425 pivot_table_set_look (pt, look);
426 pivot_table_look_unref (look);
429 static enum table_stroke
430 read_stroke (struct lexer *lexer)
432 for (int stroke = 0; stroke < TABLE_N_STROKES; stroke++)
433 if (lex_match_id (lexer, table_stroke_to_string (stroke)))
436 lex_error (lexer, "expecting stroke");
441 read_border (struct lexer *lexer, struct pivot_table *pt)
443 static const char *const pivot_border_ids[PIVOT_N_BORDERS] = {
444 [PIVOT_BORDER_TITLE] = "title",
445 [PIVOT_BORDER_OUTER_LEFT] = "outer-left",
446 [PIVOT_BORDER_OUTER_TOP] = "outer-top",
447 [PIVOT_BORDER_OUTER_RIGHT] = "outer-right",
448 [PIVOT_BORDER_OUTER_BOTTOM] = "outer-bottom",
449 [PIVOT_BORDER_INNER_LEFT] = "inner-left",
450 [PIVOT_BORDER_INNER_TOP] = "inner-top",
451 [PIVOT_BORDER_INNER_RIGHT] = "inner-right",
452 [PIVOT_BORDER_INNER_BOTTOM] = "inner-bottom",
453 [PIVOT_BORDER_DATA_LEFT] = "data-left",
454 [PIVOT_BORDER_DATA_TOP] = "data-top",
455 [PIVOT_BORDER_DIM_ROW_HORZ] = "dim-row-horz",
456 [PIVOT_BORDER_DIM_ROW_VERT] = "dim-row-vert",
457 [PIVOT_BORDER_DIM_COL_HORZ] = "dim-col-horz",
458 [PIVOT_BORDER_DIM_COL_VERT] = "dim-col-vert",
459 [PIVOT_BORDER_CAT_ROW_HORZ] = "cat-row-horz",
460 [PIVOT_BORDER_CAT_ROW_VERT] = "cat-row-vert",
461 [PIVOT_BORDER_CAT_COL_HORZ] = "cat-col-horz",
462 [PIVOT_BORDER_CAT_COL_VERT] = "cat-col-vert",
465 lex_match (lexer, T_EQUALS);
467 struct pivot_table_look *look = pivot_table_look_unshare (
468 pivot_table_look_ref (pt->look));
469 while (lex_token (lexer) == T_STRING)
471 char *s = xstrdup (lex_tokcstr (lexer));
473 if (!lex_force_match (lexer, T_LPAREN))
476 struct table_border_style style = TABLE_BORDER_STYLE_INITIALIZER;
477 style.stroke = read_stroke (lexer);
478 if (lex_is_string (lexer))
480 if (!parse_color__ (lex_tokcstr (lexer), &style.color))
482 msg (SE, "%s: unknown color", lex_tokcstr (lexer));
487 if (!lex_force_match (lexer, T_RPAREN))
491 for (int b = 0; b < PIVOT_N_BORDERS; b++)
493 if (!strncmp (s, pivot_border_ids[b], strlen (s)))
495 look->borders[b] = style;
501 msg (SE, "%s: no matching borders", s);
506 pivot_table_set_look (pt, look);
507 pivot_table_look_unref (look);
510 static struct pivot_table *
511 read_table (struct lexer *lexer)
513 struct pivot_table *pt = pivot_table_create__ (NULL, NULL);
514 while (lex_match (lexer, T_SLASH))
516 assert (!pivot_table_is_shared (pt));
518 if (lex_match_id (lexer, "ROW"))
519 read_dimension (lexer, pt, PIVOT_AXIS_ROW);
520 else if (lex_match_id (lexer, "COLUMN"))
521 read_dimension (lexer, pt, PIVOT_AXIS_COLUMN);
522 else if (lex_match_id (lexer, "LAYER"))
523 read_dimension (lexer, pt, PIVOT_AXIS_LAYER);
524 else if (lex_match_id (lexer, "LOOK"))
525 read_look (lexer, pt);
526 else if (lex_match_id (lexer, "ROTATE"))
528 lex_match (lexer, T_EQUALS);
529 while (lex_token (lexer) == T_ID)
530 if (!parse_bool_setting (lexer, "INNERCOLUMNS", "YES", "NO",
531 &pt->rotate_inner_column_labels)
532 && !parse_bool_setting (lexer, "OUTERROWS", "YES", "NO",
533 &pt->rotate_outer_row_labels))
536 else if (lex_match_id (lexer, "DISPLAY"))
538 lex_match (lexer, T_EQUALS);
539 while (lex_token (lexer) == T_ID)
540 if (!parse_bool_setting (lexer, "GRID", "YES", "NO",
541 &pt->show_grid_lines)
542 && !parse_bool_setting (lexer, "CAPTION", "YES", "NO",
544 && !parse_bool_setting (lexer, "TITLE", "YES", "NO",
548 else if (lex_match_id (lexer, "BORDER"))
549 read_border (lexer, pt);
552 msg (SE, "Expecting keyword");
557 if (!lex_force_match (lexer, T_ENDCMD))
563 output_msg (const struct msg *m_, void *lexer_)
565 struct lexer *lexer = lexer_;
568 if (m.file_name == NULL)
570 m.file_name = CONST_CAST (char *, lex_get_file_name (lexer));
571 m.first_line = lex_get_first_line_number (lexer, 0);
572 m.last_line = lex_get_last_line_number (lexer, 0);
575 m.command_name = output_get_command_name ();
577 message_item_submit (message_item_create (&m));
579 free (m.command_name);