--- /dev/null
+/* PSPP - a program for statistical analysis.
+ Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <errno.h>
+#include <getopt.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "data/file-handle-def.h"
+#include "language/lexer/lexer.h"
+#include "libpspp/assertion.h"
+#include "libpspp/compiler.h"
+#include "libpspp/i18n.h"
+#include "libpspp/string-map.h"
+#include "output/driver.h"
+#include "output/pivot-table.h"
+#include "output/table-item.h"
+
+#include "gl/error.h"
+#include "gl/progname.h"
+#include "gl/xalloc.h"
+#include "gl/xvasprintf.h"
+
+/* --emphasis: Enable emphasis in ASCII driver? */
+static bool emphasis;
+
+/* --box: ASCII driver box option. */
+static char *box;
+
+/* -o, --output: Base name for output files. */
+static const char *output_base = "render";
+
+static const char *parse_options (int argc, char **argv);
+static void usage (void) NO_RETURN;
+static struct pivot_table *read_table (struct lexer *);
+
+int
+main (int argc, char **argv)
+{
+ const char *input_file_name;
+
+ set_program_name (argv[0]);
+ i18n_init ();
+ output_engine_push ();
+ input_file_name = parse_options (argc, argv);
+
+ settings_init ();
+
+ struct lex_reader *reader = lex_reader_for_file (input_file_name, NULL,
+ LEX_SYNTAX_AUTO,
+ LEX_ERROR_CONTINUE);
+ if (!reader)
+ exit (1);
+
+ struct lexer *lexer = lex_create ();
+ lex_include (lexer, reader);
+ lex_get (lexer);
+
+ for (;;)
+ {
+ while (lex_match (lexer, T_ENDCMD))
+ continue;
+ if (lex_match (lexer, T_STOP))
+ break;
+
+ struct pivot_table *pt = read_table (lexer);
+ pivot_table_dump (pt, 0);
+ pivot_table_submit (pt);
+ }
+
+ lex_destroy (lexer);
+ output_engine_pop ();
+ fh_done ();
+
+ return 0;
+}
+
+static void PRINTF_FORMAT (2, 3)
+register_driver (struct string_map *options,
+ const char *output_file, ...)
+{
+ va_list args;
+ va_start (args, output_file);
+ string_map_insert_nocopy (options, xstrdup ("output-file"),
+ xvasprintf (output_file, args));
+ va_end (args);
+
+ struct output_driver *driver = output_driver_create (options);
+ if (driver == NULL)
+ exit (EXIT_FAILURE);
+ output_driver_register (driver);
+}
+
+static void
+configure_drivers (int width, int length, int min_break)
+{
+ /* Render to stdout. */
+ struct string_map options = STRING_MAP_INITIALIZER (options);
+ string_map_insert (&options, "format", "txt");
+ string_map_insert_nocopy (&options, xstrdup ("width"),
+ xasprintf ("%d", width));
+ if (min_break >= 0)
+ string_map_insert_nocopy (&options, xstrdup ("min-hbreak"),
+ xasprintf ("%d", min_break));
+ string_map_insert (&options, "emphasis", emphasis ? "true" : "false");
+ if (box != NULL)
+ string_map_insert (&options, "box", box);
+ register_driver (&options, "-");
+
+ /* Render to <base>.txt. */
+ register_driver (&options, "%s.txt", output_base);
+
+#ifdef HAVE_CAIRO
+ /* Render to <base>.pdf. */
+ string_map_insert (&options, "top-margin", "0");
+ string_map_insert (&options, "bottom-margin", "0");
+ string_map_insert (&options, "left-margin", "0");
+ string_map_insert (&options, "right-margin", "0");
+ string_map_insert_nocopy (&options, xstrdup ("paper-size"),
+ xasprintf ("%dx%dpt", width * 5, length * 8));
+ if (min_break >= 0)
+ {
+ string_map_insert_nocopy (&options, xstrdup ("min-hbreak"),
+ xasprintf ("%d", min_break * 5));
+ string_map_insert_nocopy (&options, xstrdup ("min-vbreak"),
+ xasprintf ("%d", min_break * 8));
+ }
+ register_driver (&options, "%s.pdf", output_base);
+#endif
+
+ /* Render to <base>.csv. */
+ register_driver (&options, "%s.csv", output_base);
+
+ /* Render to <base>.odt. */
+ register_driver (&options, "%s.odt", output_base);
+
+ string_map_destroy (&options);
+}
+
+static const char *
+parse_options (int argc, char **argv)
+{
+ int width = 79;
+ int length = 66;
+ int min_break = -1;
+
+ for (;;)
+ {
+ enum {
+ OPT_WIDTH = UCHAR_MAX + 1,
+ OPT_LENGTH,
+ OPT_MIN_BREAK,
+ OPT_EMPHASIS,
+ OPT_BOX,
+ OPT_HELP
+ };
+ static const struct option options[] =
+ {
+ {"width", required_argument, NULL, OPT_WIDTH},
+ {"length", required_argument, NULL, OPT_LENGTH},
+ {"min-break", required_argument, NULL, OPT_MIN_BREAK},
+ {"emphasis", no_argument, NULL, OPT_EMPHASIS},
+ {"box", required_argument, NULL, OPT_BOX},
+ {"output", required_argument, NULL, 'o'},
+ {"help", no_argument, NULL, OPT_HELP},
+ {NULL, 0, NULL, 0},
+ };
+
+ int c = getopt_long (argc, argv, "o:", options, NULL);
+ if (c == -1)
+ break;
+
+ switch (c)
+ {
+ case OPT_WIDTH:
+ width = atoi (optarg);
+ break;
+
+ case OPT_LENGTH:
+ length = atoi (optarg);
+ break;
+
+ case OPT_MIN_BREAK:
+ min_break = atoi (optarg);
+ break;
+
+ case OPT_EMPHASIS:
+ emphasis = true;
+ break;
+
+ case OPT_BOX:
+ box = optarg;
+ break;
+
+ case 'o':
+ output_base = optarg;
+ break;
+
+ case OPT_HELP:
+ usage ();
+
+ case 0:
+ break;
+
+ case '?':
+ exit(EXIT_FAILURE);
+ break;
+
+ default:
+ NOT_REACHED ();
+ }
+
+ }
+
+ configure_drivers (width, length, min_break);
+
+ if (optind + 1 != argc)
+ error (1, 0, "exactly one non-option argument required; "
+ "use --help for help");
+ return argv[optind];
+}
+
+static void
+usage (void)
+{
+ printf ("%s, to test rendering of PSPP tables\n"
+ "usage: %s [OPTIONS] INPUT\n"
+ "\nOptions:\n"
+ " --width=WIDTH set page width in characters\n"
+ " --length=LINE set page length in lines\n",
+ program_name, program_name);
+ exit (EXIT_SUCCESS);
+}
+
+static struct pivot_value *
+read_value (struct lexer *lexer)
+{
+ struct pivot_value *value;
+ if (lex_is_number (lexer))
+ {
+ value = pivot_value_new_number (lex_number (lexer));
+ lex_get (lexer);
+ }
+ else if (lex_is_string (lexer))
+ {
+ value = pivot_value_new_user_text (lex_tokcstr (lexer), SIZE_MAX);
+ lex_get (lexer);
+ }
+ else
+ {
+ msg (SE, "Expecting pivot_value");
+ exit (1);
+ }
+
+ if (!lex_match (lexer, T_LBRACK))
+ return value;
+ do
+ {
+ msg (SE, "Options not yet supported");
+ exit (1);
+ }
+ while (lex_match (lexer, T_COMMA));
+ if (!lex_force_match (lexer, T_RBRACK))
+ exit (1);
+}
+
+static void
+read_group (struct lexer *lexer, struct pivot_table *pt,
+ struct pivot_category *group)
+{
+ if (lex_match (lexer, T_ASTERISK))
+ group->show_label = true;
+
+ if (!lex_force_match (lexer, T_LPAREN))
+ exit (1);
+
+ if (lex_match (lexer, T_RPAREN))
+ return;
+
+ do
+ {
+ struct pivot_value *name = read_value (lexer);
+ if (lex_token (lexer) == T_ASTERISK
+ || lex_token (lexer) == T_LPAREN)
+ read_group (lexer, pt, pivot_category_create_group__ (group, name));
+ else
+ {
+ char *rc;
+ if (lex_token (lexer) == T_ID
+ && is_pivot_result_class (lex_tokcstr (lexer)))
+ {
+ rc = xstrdup (lex_tokcstr (lexer));
+ lex_get (lexer);
+ }
+ else
+ rc = NULL;
+
+ pivot_category_create_leaf_rc (group, name, rc);
+
+ free (rc);
+ }
+ }
+ while (lex_match (lexer, T_COMMA));
+ if (!lex_force_match (lexer, T_RPAREN))
+ exit (1);
+}
+
+static void
+read_dimension (struct lexer *lexer, struct pivot_table *pt,
+ enum pivot_axis_type a)
+{
+ lex_match (lexer, T_EQUALS);
+
+ struct pivot_value *name = read_value (lexer);
+ struct pivot_dimension *dim = pivot_dimension_create__ (pt, a, name);
+ read_group (lexer, pt, dim->root);
+}
+
+static void
+read_look (struct lexer *lexer, struct pivot_table *pt)
+{
+ lex_match (lexer, T_EQUALS);
+
+ if (lex_is_string (lexer))
+ {
+ struct pivot_table_look *look;
+ char *error = pivot_table_look_read (lex_tokcstr (lexer), &look);
+ if (error)
+ {
+ msg (SE, "%s", error);
+ exit (1);
+ }
+
+ pivot_table_set_look (pt, look);
+ pivot_table_look_unref (look);
+ }
+}
+
+static struct pivot_table *
+read_table (struct lexer *lexer)
+{
+ struct pivot_table *pt = pivot_table_create__ (NULL, NULL);
+ while (lex_match (lexer, T_SLASH))
+ {
+ if (lex_match_id (lexer, "ROW"))
+ read_dimension (lexer, pt, PIVOT_AXIS_ROW);
+ else if (lex_match_id (lexer, "COLUMN"))
+ read_dimension (lexer, pt, PIVOT_AXIS_COLUMN);
+ else if (lex_match_id (lexer, "LAYER"))
+ read_dimension (lexer, pt, PIVOT_AXIS_LAYER);
+ else if (lex_match_id (lexer, "LOOK"))
+ read_look (lexer, pt);
+ else
+ {
+ msg (SE, "Expecting keyword");
+ exit (1);
+ }
+ }
+
+ if (!lex_force_match (lexer, T_ENDCMD))
+ exit (1);
+ return pt;
+}