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 "language/lexer/format-parser.h"
29 #include "libpspp/assertion.h"
30 #include "libpspp/compiler.h"
31 #include "libpspp/i18n.h"
32 #include "libpspp/string-map.h"
33 #include "output/driver.h"
34 #include "output/message-item.h"
35 #include "output/options.h"
36 #include "output/pivot-table.h"
37 #include "output/table-item.h"
40 #include "gl/progname.h"
41 #include "gl/xalloc.h"
42 #include "gl/xvasprintf.h"
44 /* --emphasis: Enable emphasis in ASCII driver? */
47 /* --box: ASCII driver box option. */
50 /* -o, --output: Base name for output files. */
51 static const char *output_base = "render";
53 static const char *parse_options (int argc, char **argv);
54 static void usage (void) NO_RETURN;
55 static struct pivot_table *read_table (struct lexer *);
56 static void output_msg (const struct msg *, void *);
59 main (int argc, char **argv)
61 const char *input_file_name;
63 set_program_name (argv[0]);
65 output_engine_push ();
66 input_file_name = parse_options (argc, argv);
70 struct lex_reader *reader = lex_reader_for_file (input_file_name, NULL,
76 struct lexer *lexer = lex_create ();
77 msg_set_handler (output_msg, lexer);
78 lex_include (lexer, reader);
83 while (lex_match (lexer, T_ENDCMD))
85 if (lex_match (lexer, T_STOP))
88 struct pivot_table *pt = read_table (lexer);
89 pivot_table_dump (pt, 0);
90 pivot_table_submit (pt);
100 static void PRINTF_FORMAT (2, 3)
101 register_driver (struct string_map *options,
102 const char *output_file, ...)
105 va_start (args, output_file);
106 string_map_insert_nocopy (options, xstrdup ("output-file"),
107 xvasprintf (output_file, args));
110 struct output_driver *driver = output_driver_create (options);
113 output_driver_register (driver);
117 configure_drivers (int width, int length, int min_break)
119 /* Render to stdout. */
120 struct string_map options = STRING_MAP_INITIALIZER (options);
121 string_map_insert (&options, "format", "txt");
122 string_map_insert_nocopy (&options, xstrdup ("width"),
123 xasprintf ("%d", width));
125 string_map_insert_nocopy (&options, xstrdup ("min-hbreak"),
126 xasprintf ("%d", min_break));
127 string_map_insert (&options, "emphasis", emphasis ? "true" : "false");
129 string_map_insert (&options, "box", box);
130 register_driver (&options, "-");
134 /* Render to <base>.pdf. */
135 string_map_insert (&options, "top-margin", "0");
136 string_map_insert (&options, "bottom-margin", "0");
137 string_map_insert (&options, "left-margin", "0");
138 string_map_insert (&options, "right-margin", "0");
139 string_map_insert_nocopy (&options, xstrdup ("paper-size"),
140 xasprintf ("%dx%dpt", width * 5, length * 8));
143 string_map_insert_nocopy (&options, xstrdup ("min-hbreak"),
144 xasprintf ("%d", min_break * 5));
145 string_map_insert_nocopy (&options, xstrdup ("min-vbreak"),
146 xasprintf ("%d", min_break * 8));
148 register_driver (&options, "%s.pdf", output_base);
151 register_driver (&options, "%s.txt", output_base);
152 register_driver (&options, "%s.csv", output_base);
153 register_driver (&options, "%s.odt", output_base);
154 register_driver (&options, "%s.spv", output_base);
156 string_map_destroy (&options);
160 parse_options (int argc, char **argv)
169 OPT_WIDTH = UCHAR_MAX + 1,
177 static const struct option options[] =
179 {"width", required_argument, NULL, OPT_WIDTH},
180 {"length", required_argument, NULL, OPT_LENGTH},
181 {"min-break", required_argument, NULL, OPT_MIN_BREAK},
182 {"emphasis", no_argument, NULL, OPT_EMPHASIS},
183 {"box", required_argument, NULL, OPT_BOX},
184 {"output", required_argument, NULL, 'o'},
185 {"table-look", required_argument, NULL, OPT_TABLE_LOOK},
186 {"help", no_argument, NULL, OPT_HELP},
190 int c = getopt_long (argc, argv, "o:", options, NULL);
197 width = atoi (optarg);
201 length = atoi (optarg);
205 min_break = atoi (optarg);
217 output_base = optarg;
222 struct pivot_table_look *look;
223 char *err = pivot_table_look_read (optarg, &look);
225 error (1, 0, "%s", err);
226 pivot_table_look_set_default (look);
227 pivot_table_look_unref (look);
247 configure_drivers (width, length, min_break);
249 if (optind + 1 != argc)
250 error (1, 0, "exactly one non-option argument required; "
251 "use --help for help");
258 printf ("%s, to test rendering of PSPP tables\n"
259 "usage: %s [OPTIONS] INPUT\n"
261 " --width=WIDTH set page width in characters\n"
262 " --length=LINE set page length in lines\n",
263 program_name, program_name);
268 parse_settings_value_show (struct lexer *lexer, const char *name,
269 enum settings_value_show *show)
271 if (lex_match_id (lexer, name))
273 if (!lex_force_match (lexer, T_EQUALS))
276 if (lex_match_id (lexer, "DEFAULT"))
277 *show = SETTINGS_VALUE_SHOW_DEFAULT;
278 else if (lex_match_id (lexer, "VALUE"))
279 *show = SETTINGS_VALUE_SHOW_VALUE;
280 else if (lex_match_id (lexer, "LABEL"))
281 *show = SETTINGS_VALUE_SHOW_LABEL;
282 else if (lex_match_id (lexer, "BOTH"))
283 *show = SETTINGS_VALUE_SHOW_BOTH;
286 lex_error_expecting (lexer, "DEFAULT", "VALUE", "LABEL", "BOTH");
297 parse_string_setting (struct lexer *lexer, const char *name, char **stringp)
299 if (lex_match_id (lexer, name))
301 lex_match (lexer, T_EQUALS);
302 if (!lex_force_string (lexer))
306 *stringp = xstrdup (lex_tokcstr (lexer));
316 read_value_option (struct lexer *lexer, struct pivot_value *value)
318 enum settings_value_show *show
319 = (value->type == PIVOT_VALUE_NUMERIC ? &value->numeric.show
320 : value->type == PIVOT_VALUE_STRING ? &value->string.show
321 : value->type == PIVOT_VALUE_VARIABLE ? &value->variable.show
323 if (show && parse_settings_value_show (lexer, "SHOW", show))
327 = (value->type == PIVOT_VALUE_NUMERIC ? &value->numeric.var_name
328 : value->type == PIVOT_VALUE_STRING ? &value->string.var_name
330 if (var_name && parse_string_setting (lexer, "VAR", var_name))
334 = (value->type == PIVOT_VALUE_NUMERIC ? &value->numeric.value_label
335 : value->type == PIVOT_VALUE_STRING ? &value->string.value_label
336 : value->type == PIVOT_VALUE_VARIABLE ? &value->variable.var_label
338 if (label && parse_string_setting (lexer, "LABEL", label))
341 if (value->type == PIVOT_VALUE_STRING && lex_match_id (lexer, "HEX"))
343 value->string.hex = true;
347 if (value->type == PIVOT_VALUE_NUMERIC)
351 bool ok = parse_format_specifier (lexer, &fmt);
356 if (!fmt_check_output (&fmt)
357 || !fmt_check_type_compat (&fmt, VAL_NUMERIC))
360 value->numeric.format = fmt;
365 if (parse_string_setting (lexer, "SUPERSCRIPT", &value->superscript))
368 if (lex_match_id (lexer, "SUBSCRIPTS"))
370 lex_match (lexer, T_EQUALS);
371 size_t allocated_subscripts = value->n_subscripts;
372 while (lex_token (lexer) == T_STRING)
374 if (value->n_subscripts >= allocated_subscripts)
375 value->subscripts = x2nrealloc (value->subscripts,
376 &allocated_subscripts,
377 sizeof *value->subscripts);
379 value->subscripts[value->n_subscripts++] = xstrdup (
380 lex_tokcstr (lexer));
386 lex_error (lexer, "Expecting valid value option");
390 static struct pivot_value *
391 read_value (struct lexer *lexer)
393 struct pivot_value *value;
394 if (lex_is_number (lexer))
396 value = pivot_value_new_number (lex_number (lexer));
399 else if (lex_is_string (lexer))
401 value = xmalloc (sizeof *value);
402 *value = (struct pivot_value) {
403 .type = PIVOT_VALUE_STRING,
404 .string = { .s = xstrdup (lex_tokcstr (lexer)) },
408 else if (lex_token (lexer) == T_ID)
410 value = xmalloc (sizeof *value);
411 *value = (struct pivot_value) {
412 .type = PIVOT_VALUE_VARIABLE,
413 .variable = { .var_name = xstrdup (lex_tokcstr (lexer)) },
419 msg (SE, "Expecting pivot_value");
423 while (lex_match (lexer, T_LBRACK))
425 read_value_option (lexer, value);
427 if (!lex_force_match (lexer, T_RBRACK))
435 read_group (struct lexer *lexer, struct pivot_table *pt,
436 struct pivot_category *group)
438 if (lex_match (lexer, T_ASTERISK))
439 group->show_label = true;
441 if (!lex_force_match (lexer, T_LPAREN))
444 if (lex_match (lexer, T_RPAREN))
449 struct pivot_value *name = read_value (lexer);
450 if (lex_token (lexer) == T_ASTERISK
451 || lex_token (lexer) == T_LPAREN)
452 read_group (lexer, pt, pivot_category_create_group__ (group, name));
456 if (lex_token (lexer) == T_ID
457 && is_pivot_result_class (lex_tokcstr (lexer)))
459 rc = xstrdup (lex_tokcstr (lexer));
465 pivot_category_create_leaf_rc (group, name, rc);
470 while (lex_match (lexer, T_COMMA));
471 if (!lex_force_match (lexer, T_RPAREN))
476 read_dimension (struct lexer *lexer, struct pivot_table *pt,
477 enum pivot_axis_type a)
479 lex_match (lexer, T_EQUALS);
481 struct pivot_value *name = read_value (lexer);
482 struct pivot_dimension *dim = pivot_dimension_create__ (pt, a, name);
483 read_group (lexer, pt, dim->root);
487 parse_bool_setting (struct lexer *lexer, const char *name,
488 const char *true_kw, const char *false_kw,
491 if (lex_match_id (lexer, name))
493 if (!lex_force_match (lexer, T_EQUALS))
496 if (lex_match_id (lexer, true_kw))
498 else if (lex_match_id (lexer, false_kw))
502 lex_error_expecting (lexer, true_kw, false_kw);
513 read_look (struct lexer *lexer, struct pivot_table *pt)
515 lex_match (lexer, T_EQUALS);
517 if (lex_is_string (lexer))
519 struct pivot_table_look *look;
520 char *error = pivot_table_look_read (lex_tokcstr (lexer), &look);
523 msg (SE, "%s", error);
528 pivot_table_set_look (pt, look);
529 pivot_table_look_unref (look);
532 struct pivot_table_look *look = pivot_table_look_unshare (
533 pivot_table_look_ref (pt->look));
536 if (!parse_bool_setting (lexer, "EMPTY", "HIDE", "SHOW",
538 && !parse_bool_setting (lexer, "ROWLABELS", "CORNER", "NESTED",
539 &look->row_labels_in_corner)
540 && !parse_bool_setting (lexer, "MARKERS", "NUMERIC", "ALPHA",
541 &look->show_numeric_markers)
542 && !parse_bool_setting (lexer, "LEVEL", "SUPER", "SUB",
543 &look->footnote_marker_superscripts)
544 && !parse_bool_setting (lexer, "LAYERS", "CURRENT", "ALL",
545 &look->print_all_layers)
546 && !parse_bool_setting (lexer, "PAGINATELAYERS", "YES", "NO",
547 &look->paginate_layers)
548 && !parse_bool_setting (lexer, "HSHRINK", "YES", "NO",
549 &look->shrink_to_fit[TABLE_HORZ])
550 && !parse_bool_setting (lexer, "VSHRINK", "YES", "NO",
551 &look->shrink_to_fit[TABLE_VERT])
552 && !parse_bool_setting (lexer, "TOPCONTINUATION", "YES", "NO",
553 &look->top_continuation)
554 && !parse_bool_setting (lexer, "BOTTOMCONTINUATION", "YES", "NO",
555 &look->bottom_continuation)
556 && !parse_string_setting (lexer, "CONTINUATION",
557 &look->continuation))
560 pivot_table_set_look (pt, look);
561 pivot_table_look_unref (look);
564 static enum table_stroke
565 read_stroke (struct lexer *lexer)
567 for (int stroke = 0; stroke < TABLE_N_STROKES; stroke++)
568 if (lex_match_id (lexer, table_stroke_to_string (stroke)))
571 lex_error (lexer, "expecting stroke");
575 static struct cell_color
576 read_color (struct lexer *lexer)
578 struct cell_color color;
579 if (!parse_color__ (lex_tokcstr (lexer), &color))
581 msg (SE, "%s: unknown color", lex_tokcstr (lexer));
589 parse_value_setting (struct lexer *lexer, const char *name,
590 struct pivot_value **valuep)
592 if (lex_match_id (lexer, name))
594 lex_match (lexer, T_EQUALS);
596 pivot_value_destroy (*valuep);
597 *valuep = read_value (lexer);
606 read_border (struct lexer *lexer, struct pivot_table *pt)
608 static const char *const pivot_border_ids[PIVOT_N_BORDERS] = {
609 [PIVOT_BORDER_TITLE] = "title",
610 [PIVOT_BORDER_OUTER_LEFT] = "outer-left",
611 [PIVOT_BORDER_OUTER_TOP] = "outer-top",
612 [PIVOT_BORDER_OUTER_RIGHT] = "outer-right",
613 [PIVOT_BORDER_OUTER_BOTTOM] = "outer-bottom",
614 [PIVOT_BORDER_INNER_LEFT] = "inner-left",
615 [PIVOT_BORDER_INNER_TOP] = "inner-top",
616 [PIVOT_BORDER_INNER_RIGHT] = "inner-right",
617 [PIVOT_BORDER_INNER_BOTTOM] = "inner-bottom",
618 [PIVOT_BORDER_DATA_LEFT] = "data-left",
619 [PIVOT_BORDER_DATA_TOP] = "data-top",
620 [PIVOT_BORDER_DIM_ROW_HORZ] = "dim-row-horz",
621 [PIVOT_BORDER_DIM_ROW_VERT] = "dim-row-vert",
622 [PIVOT_BORDER_DIM_COL_HORZ] = "dim-col-horz",
623 [PIVOT_BORDER_DIM_COL_VERT] = "dim-col-vert",
624 [PIVOT_BORDER_CAT_ROW_HORZ] = "cat-row-horz",
625 [PIVOT_BORDER_CAT_ROW_VERT] = "cat-row-vert",
626 [PIVOT_BORDER_CAT_COL_HORZ] = "cat-col-horz",
627 [PIVOT_BORDER_CAT_COL_VERT] = "cat-col-vert",
630 lex_match (lexer, T_EQUALS);
632 struct pivot_table_look *look = pivot_table_look_unshare (
633 pivot_table_look_ref (pt->look));
634 while (lex_token (lexer) == T_STRING)
636 char *s = xstrdup (lex_tokcstr (lexer));
638 if (!lex_force_match (lexer, T_LPAREN))
641 struct table_border_style style = TABLE_BORDER_STYLE_INITIALIZER;
642 style.stroke = read_stroke (lexer);
643 if (lex_is_string (lexer))
644 style.color = read_color (lexer);
645 if (!lex_force_match (lexer, T_RPAREN))
649 for (int b = 0; b < PIVOT_N_BORDERS; b++)
651 if (!strncmp (s, pivot_border_ids[b], strlen (s)))
653 look->borders[b] = style;
659 msg (SE, "%s: no matching borders", s);
664 pivot_table_set_look (pt, look);
665 pivot_table_look_unref (look);
668 static struct pivot_table *
669 read_table (struct lexer *lexer)
671 struct pivot_table *pt = pivot_table_create__ (NULL, NULL);
672 while (lex_match (lexer, T_SLASH))
674 assert (!pivot_table_is_shared (pt));
676 if (lex_match_id (lexer, "ROW"))
677 read_dimension (lexer, pt, PIVOT_AXIS_ROW);
678 else if (lex_match_id (lexer, "COLUMN"))
679 read_dimension (lexer, pt, PIVOT_AXIS_COLUMN);
680 else if (lex_match_id (lexer, "LAYER"))
681 read_dimension (lexer, pt, PIVOT_AXIS_LAYER);
682 else if (lex_match_id (lexer, "LOOK"))
683 read_look (lexer, pt);
684 else if (lex_match_id (lexer, "ROTATE"))
686 lex_match (lexer, T_EQUALS);
687 while (lex_token (lexer) == T_ID)
688 if (!parse_bool_setting (lexer, "INNERCOLUMNS", "YES", "NO",
689 &pt->rotate_inner_column_labels)
690 && !parse_bool_setting (lexer, "OUTERROWS", "YES", "NO",
691 &pt->rotate_outer_row_labels))
694 else if (lex_match_id (lexer, "DISPLAY"))
696 lex_match (lexer, T_EQUALS);
697 while (lex_token (lexer) == T_ID)
699 if (parse_bool_setting (lexer, "GRID", "YES", "NO",
700 &pt->show_grid_lines)
701 || parse_bool_setting (lexer, "CAPTION", "YES", "NO",
703 || parse_bool_setting (lexer, "TITLE", "YES", "NO",
707 if (parse_settings_value_show (lexer, "VALUES", &pt->show_values)
708 || parse_settings_value_show (lexer, "VARIABLES",
709 &pt->show_variables))
715 else if (parse_value_setting (lexer, "TITLE", &pt->title)
716 || parse_value_setting (lexer, "SUBTYPE", &pt->subtype)
717 || parse_value_setting (lexer, "CORNER", &pt->corner_text)
718 || parse_value_setting (lexer, "CAPTION", &pt->caption)
719 || parse_string_setting (lexer, "NOTES", &pt->notes))
723 else if (lex_match_id (lexer, "BORDER"))
724 read_border (lexer, pt);
727 msg (SE, "Expecting keyword");
732 size_t *dindexes = xcalloc (pt->n_dimensions, sizeof *dindexes);
733 for (size_t i = 0; ; i++)
735 pivot_table_put (pt, dindexes, pt->n_dimensions,
736 pivot_value_new_integer (i));
738 for (size_t j = 0; j < pt->n_dimensions; j++)
740 if (++dindexes[j] < pt->dimensions[j]->n_leaves)
749 if (!lex_force_match (lexer, T_ENDCMD))
755 output_msg (const struct msg *m_, void *lexer_)
757 struct lexer *lexer = lexer_;
760 if (m.file_name == NULL)
762 m.file_name = CONST_CAST (char *, lex_get_file_name (lexer));
763 m.first_line = lex_get_first_line_number (lexer, 0);
764 m.last_line = lex_get_last_line_number (lexer, 0);
767 m.command_name = output_get_command_name ();
769 message_item_submit (message_item_create (&m));
771 free (m.command_name);