1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011, 2012 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/>. */
22 #include "data/case.h"
23 #include "data/dataset.h"
24 #include "data/data-out.h"
25 #include "data/format.h"
26 #include "data/transformations.h"
27 #include "data/variable.h"
28 #include "language/command.h"
29 #include "language/commands/data-writer.h"
30 #include "language/commands/file-handle.h"
31 #include "language/commands/placement-parser.h"
32 #include "language/lexer/format-parser.h"
33 #include "language/lexer/lexer.h"
34 #include "language/lexer/variable-parser.h"
35 #include "libpspp/assertion.h"
36 #include "libpspp/compiler.h"
37 #include "libpspp/i18n.h"
38 #include "libpspp/message.h"
39 #include "libpspp/misc.h"
40 #include "libpspp/pool.h"
41 #include "libpspp/u8-line.h"
42 #include "output/driver.h"
43 #include "output/pivot-table.h"
44 #include "output/output-item.h"
46 #include "gl/xalloc.h"
49 #define N_(msgid) msgid
50 #define _(msgid) gettext (msgid)
52 /* Describes what to do when an output field is encountered. */
55 PRT_LITERAL, /* Literal string. */
56 PRT_VAR /* Variable. */
59 /* Describes how to output one field. */
63 enum field_type type; /* What type of field this is. */
64 int record; /* 1-based record number. */
65 int first_column; /* 0-based first column. */
66 int start_ofs, end_ofs;
69 const struct variable *var; /* Associated variable. */
70 struct fmt_spec format; /* Output spec. */
71 bool add_space; /* Add trailing space? */
72 bool sysmis_as_spaces; /* Output SYSMIS as spaces? */
74 /* PRT_LITERAL only. */
75 struct substring string; /* String to output. */
76 int width; /* Width of 'string', in display columns. */
79 /* PRINT, PRINT EJECT, WRITE private data structure. */
82 struct pool *pool; /* Stores related data. */
83 bool eject; /* Eject page before printing? */
84 bool include_prefix; /* Prefix lines with space? */
85 const char *encoding; /* Encoding to use for output. */
86 struct dfm_writer *writer; /* Output file, NULL=listing file. */
87 struct prt_out_spec *specs;
89 size_t n_records; /* Number of records to write. */
98 static const struct trns_class print_binary_trns_class;
99 static const struct trns_class print_text_trns_class;
101 static int cmd_print__ (struct lexer *, struct dataset *,
102 enum which_formats, bool eject);
103 static bool parse_specs (struct lexer *, struct pool *tmp_pool,
104 struct print_trns *, int records_ofs,
105 struct dictionary *, enum which_formats);
106 static void dump_table (struct print_trns *);
108 static bool print_trns_free (void *trns_);
110 static const struct prt_out_spec *find_binary_spec (const struct print_trns *);
114 /* Parses PRINT command. */
116 cmd_print (struct lexer *lexer, struct dataset *ds)
118 return cmd_print__ (lexer, ds, PRINT, false);
121 /* Parses PRINT EJECT command. */
123 cmd_print_eject (struct lexer *lexer, struct dataset *ds)
125 return cmd_print__ (lexer, ds, PRINT, true);
128 /* Parses WRITE command. */
130 cmd_write (struct lexer *lexer, struct dataset *ds)
132 return cmd_print__ (lexer, ds, WRITE, false);
135 /* Parses the output commands. */
137 cmd_print__ (struct lexer *lexer, struct dataset *ds,
138 enum which_formats which_formats, bool eject)
140 bool print_table = false;
141 struct file_handle *fh = NULL;
142 char *encoding = NULL;
144 /* Fill in prt to facilitate error-handling. */
145 struct pool *pool = pool_create ();
146 struct print_trns *trns = pool_alloc (pool, sizeof *trns);
147 *trns = (struct print_trns) { .pool = pool, .eject = eject };
148 struct pool *tmp_pool = pool_create_subpool (trns->pool);
150 /* Parse the command options. */
152 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
154 if (lex_match_id (lexer, "OUTFILE"))
156 lex_match (lexer, T_EQUALS);
158 fh = fh_parse (lexer, FH_REF_FILE, NULL);
162 else if (lex_match_id (lexer, "ENCODING"))
164 lex_match (lexer, T_EQUALS);
165 if (!lex_force_string (lexer))
169 encoding = ss_xstrdup (lex_tokss (lexer));
173 else if (lex_match_id (lexer, "RECORDS"))
175 lex_match (lexer, T_EQUALS);
176 lex_match (lexer, T_LPAREN);
177 if (!lex_force_int_range (lexer, "RECORDS", 0, INT_MAX))
179 trns->n_records = lex_integer (lexer);
180 records_ofs = lex_ofs (lexer);
182 lex_match (lexer, T_RPAREN);
184 else if (lex_match_id (lexer, "TABLE"))
186 else if (lex_match_id (lexer, "NOTABLE"))
190 lex_error_expecting (lexer, "OUTFILE", "ENCODING", "RECORDS",
196 /* When PRINT or PRINT EJECT writes to an external file, we
197 prefix each line with a space for compatibility. */
198 trns->include_prefix = which_formats == PRINT && fh != NULL;
200 /* Parse variables and strings. */
201 if (!parse_specs (lexer, tmp_pool, trns, records_ofs,
202 dataset_dict (ds), which_formats))
205 /* Are there any binary formats?
207 There are real difficulties figuring out what to do when both binary
208 formats and nontrivial encodings enter the picture. So when binary
209 formats are present we fall back to much simpler handling. */
210 const struct prt_out_spec *binary_spec = find_binary_spec (trns);
211 if (binary_spec && !fh)
213 lex_ofs_error (lexer, binary_spec->start_ofs, binary_spec->end_ofs,
214 _("%s is required when binary formats are specified."),
219 if (lex_end_of_command (lexer) != CMD_SUCCESS)
224 trns->writer = dfm_open_writer (fh, encoding);
225 if (trns->writer == NULL)
227 trns->encoding = dfm_writer_get_encoding (trns->writer);
230 trns->encoding = UTF8;
232 /* Output the variable table if requested. */
236 /* Put the transformation in the queue. */
237 add_transformation (ds, (binary_spec
238 ? &print_binary_trns_class
239 : &print_text_trns_class), trns);
241 pool_destroy (tmp_pool);
247 print_trns_free (trns);
252 static bool parse_string_argument (struct lexer *, struct print_trns *,
253 size_t *allocated_specs,
254 int record, int *column);
255 static bool parse_variable_argument (struct lexer *, const struct dictionary *,
257 size_t *allocated_specs,
258 struct pool *tmp_pool,
259 int *record, int *column,
262 /* Parses all the variable and string specifications on a single
263 PRINT, PRINT EJECT, or WRITE command into the prt structure.
266 parse_specs (struct lexer *lexer, struct pool *tmp_pool,
267 struct print_trns *trns, int records_ofs, struct dictionary *dict,
268 enum which_formats which_formats)
273 if (lex_token (lexer) == T_ENDCMD)
279 size_t allocated_specs = 0;
280 while (lex_token (lexer) != T_ENDCMD)
282 if (!parse_record_placement (lexer, &record, &column))
285 bool ok = (lex_is_string (lexer)
286 ? parse_string_argument (lexer, trns, &allocated_specs,
288 : parse_variable_argument (lexer, dict, trns, &allocated_specs,
289 tmp_pool, &record, &column,
294 lex_match (lexer, T_COMMA);
297 if (trns->n_records != 0 && trns->n_records != record)
298 lex_ofs_error (lexer, records_ofs, records_ofs,
299 _("Output calls for %d records but %zu specified on RECORDS "
301 record, trns->n_records);
302 trns->n_records = record;
307 static struct prt_out_spec *
308 add_spec (struct print_trns *trns, size_t *allocated_specs)
310 if (trns->n_specs >= *allocated_specs)
311 trns->specs = pool_2nrealloc (trns->pool, trns->specs, allocated_specs,
312 sizeof *trns->specs);
313 return &trns->specs[trns->n_specs++];
316 /* Parses a string argument to the PRINT commands. Returns success. */
318 parse_string_argument (struct lexer *lexer, struct print_trns *trns,
319 size_t *allocated_specs, int record, int *column)
321 struct prt_out_spec *spec = add_spec (trns, allocated_specs);
322 *spec = (struct prt_out_spec) {
325 .first_column = *column,
326 .string = ss_clone_pool (lex_tokss (lexer), trns->pool),
327 .start_ofs = lex_ofs (lexer),
331 /* Parse the included column range. */
332 if (lex_is_number (lexer))
334 int first_column, last_column;
335 bool range_specified;
337 if (!parse_column_range (lexer, 1,
338 &first_column, &last_column, &range_specified))
341 spec->first_column = first_column;
345 ds_init_substring (&s, spec->string);
346 ds_set_length (&s, last_column - first_column + 1, ' ');
347 spec->string = ss_clone_pool (s.ss, trns->pool);
351 spec->end_ofs = lex_ofs (lexer) - 1;
353 spec->width = u8_width (CHAR_CAST (const uint8_t *, spec->string.string),
354 spec->string.length, UTF8);
355 *column = spec->first_column + spec->width;
360 /* Parses a variable argument to the PRINT commands by passing it off
361 to fixed_parse_compatible() or fixed_parse_fortran() as appropriate.
364 parse_variable_argument (struct lexer *lexer, const struct dictionary *dict,
365 struct print_trns *trns, size_t *allocated_specs,
366 struct pool *tmp_pool, int *record, int *column,
367 enum which_formats which_formats)
369 const struct variable **vars;
371 if (!parse_variables_const_pool (lexer, tmp_pool, dict,
372 &vars, &n_vars, PV_DUPLICATE))
375 struct fmt_spec *formats, *f;
378 int formats_start = lex_ofs (lexer);
379 if (lex_is_number (lexer) || lex_token (lexer) == T_LPAREN)
381 if (!parse_var_placements (lexer, tmp_pool, n_vars, FMT_FOR_OUTPUT,
382 &formats, &n_formats))
388 lex_match (lexer, T_ASTERISK);
390 formats = pool_nmalloc (tmp_pool, n_vars, sizeof *formats);
392 for (size_t i = 0; i < n_vars; i++)
394 const struct variable *v = vars[i];
395 formats[i] = (which_formats == PRINT
396 ? var_get_print_format (v)
397 : var_get_write_format (v));
399 add_space = which_formats == PRINT;
401 int formats_end = lex_ofs (lexer) - 1;
404 for (f = formats; f < &formats[n_formats]; f++)
405 if (!execute_placement_format (*f, record, column))
407 const struct variable *var = vars[var_idx++];
408 char *error = fmt_check_width_compat__ (*f, var_get_name (var),
409 var_get_width (var));
412 lex_ofs_error (lexer, formats_start, formats_end, "%s", error);
417 struct prt_out_spec *spec = add_spec (trns, allocated_specs);
418 *spec = (struct prt_out_spec) {
421 .first_column = *column,
424 .add_space = add_space,
426 /* This is a completely bizarre twist for compatibility: WRITE
427 outputs the system-missing value as a field filled with spaces,
428 instead of using the normal format that usually contains a
430 .sysmis_as_spaces = (which_formats == WRITE
431 && var_is_numeric (var)
432 && (fmt_get_category (f->type)
436 *column += f->w + add_space;
438 assert (var_idx == n_vars);
443 /* Prints the table produced by the TABLE subcommand to the listing
446 dump_table (struct print_trns *trns)
448 struct pivot_table *table = pivot_table_create (N_("Print Summary"));
450 pivot_dimension_create (table, PIVOT_AXIS_COLUMN, N_("Attributes"),
451 N_("Record"), N_("Columns"), N_("Format"));
453 struct pivot_dimension *variables = pivot_dimension_create (
454 table, PIVOT_AXIS_ROW, N_("Variable"));
456 for (size_t i = 0; i < trns->n_specs; i++)
458 const struct prt_out_spec *spec = &trns->specs[i];
459 if (spec->type != PRT_VAR)
462 int row = pivot_category_create_leaf (
463 variables->root, pivot_value_new_variable (spec->var));
465 pivot_table_put2 (table, 0, row,
466 pivot_value_new_integer (spec->record));
467 int last_column = spec->first_column + spec->format.w - 1;
468 pivot_table_put2 (table, 1, row, pivot_value_new_user_text_nocopy (
470 spec->first_column, last_column)));
472 char fmt_string[FMT_STRING_LEN_MAX + 1];
473 pivot_table_put2 (table, 2, row, pivot_value_new_user_text (
474 fmt_to_string (spec->format, fmt_string), -1));
477 int row = pivot_category_create_leaf (
478 variables->root, pivot_value_new_text (N_("N of Records")));
479 pivot_table_put2 (table, 0, row,
480 pivot_value_new_integer (trns->n_records));
482 pivot_table_submit (table);
485 static const struct prt_out_spec *
486 find_binary_spec (const struct print_trns *trns)
488 for (size_t i = 0; i < trns->n_specs; i++)
490 const struct prt_out_spec *spec = &trns->specs[i];
491 if (spec->type == PRT_VAR
492 && fmt_get_category (spec->format.type) == FMT_CAT_BINARY)
498 /* Transformation, for all-text output. */
500 static void print_text_flush_records (struct print_trns *, struct u8_line *,
502 bool *eject, int *record);
504 /* Performs the transformation inside print_trns T on case C. */
505 static enum trns_result
506 print_text_trns_proc (void *trns_, struct ccase **c,
507 casenumber case_num UNUSED)
509 struct print_trns *trns = trns_;
512 bool eject = trns->eject;
515 u8_line_init (&line);
516 for (size_t i = 0; i < trns->n_specs; i++)
518 const struct prt_out_spec *spec = &trns->specs[i];
519 int x0 = spec->first_column;
521 print_text_flush_records (trns, &line, spec->record, &eject, &record);
523 u8_line_set_length (&line, spec->first_column);
524 if (spec->type == PRT_VAR)
526 const union value *input = case_data (*c, spec->var);
529 if (!spec->sysmis_as_spaces || input->f != SYSMIS)
535 s = data_out (input, var_get_encoding (spec->var),
536 spec->format, settings_get_fmt_settings ());
538 width = u8_width (CHAR_CAST (const uint8_t *, s), len, UTF8);
540 u8_line_put (&line, x0, x1, s, len);
545 int n = spec->format.w;
548 memset (u8_line_reserve (&line, x0, x1, n), ' ', n);
552 *u8_line_reserve (&line, x1, x1 + 1, 1) = ' ';
556 const struct substring *s = &spec->string;
558 u8_line_put (&line, x0, x0 + spec->width, s->string, s->length);
561 print_text_flush_records (trns, &line, trns->n_records + 1,
563 u8_line_destroy (&line);
565 if (trns->writer != NULL && dfm_write_error (trns->writer))
567 return TRNS_CONTINUE;
570 /* Advance from *RECORD to TARGET_RECORD, outputting records
571 along the way. If *EJECT is true, then the first record
572 output is preceded by ejecting the page (and *EJECT is set
575 print_text_flush_records (struct print_trns *trns, struct u8_line *line,
576 int target_record, bool *eject, int *record)
578 for (; target_record > *record; (*record)++)
585 if (trns->writer == NULL)
586 output_item_submit (page_break_item_create ());
590 *u8_line_reserve (line, 0, 1, 1) = leader;
592 if (trns->writer == NULL)
593 output_log ("%s", ds_cstr (&line->s) + 1);
596 size_t len = ds_length (&line->s);
597 char *s = ds_cstr (&line->s);
599 if (!trns->include_prefix)
605 dfm_put_record_utf8 (trns->writer, s, len);
610 /* Transformation, for output involving binary. */
612 static void print_binary_flush_records (struct print_trns *,
613 struct string *line, int target_record,
614 bool *eject, int *record);
616 /* Performs the transformation inside print_trns T on case C. */
617 static enum trns_result
618 print_binary_trns_proc (void *trns_, struct ccase **c,
619 casenumber case_num UNUSED)
621 struct print_trns *trns = trns_;
622 bool eject = trns->eject;
623 char encoded_space = recode_byte (trns->encoding, C_ENCODING, ' ');
625 struct string line = DS_EMPTY_INITIALIZER;
627 ds_put_byte (&line, ' ');
628 for (size_t i = 0; i < trns->n_specs; i++)
630 const struct prt_out_spec *spec = &trns->specs[i];
631 print_binary_flush_records (trns, &line, spec->record, &eject, &record);
633 ds_set_length (&line, spec->first_column, encoded_space);
634 if (spec->type == PRT_VAR)
636 const union value *input = case_data (*c, spec->var);
637 if (!spec->sysmis_as_spaces || input->f != SYSMIS)
638 data_out_recode (input, var_get_encoding (spec->var),
639 spec->format, settings_get_fmt_settings (),
640 &line, trns->encoding);
642 ds_put_byte_multiple (&line, encoded_space, spec->format.w);
644 ds_put_byte (&line, encoded_space);
648 ds_put_substring (&line, spec->string);
649 if (0 != strcmp (trns->encoding, UTF8))
651 size_t length = spec->string.length;
652 char *data = ss_data (ds_tail (&line, length));
653 char *s = recode_string (trns->encoding, UTF8, data, length);
654 memcpy (data, s, length);
659 print_binary_flush_records (trns, &line, trns->n_records + 1,
663 if (trns->writer != NULL && dfm_write_error (trns->writer))
665 return TRNS_CONTINUE;
668 /* Advance from *RECORD to TARGET_RECORD, outputting records
669 along the way. If *EJECT is true, then the first record
670 output is preceded by ejecting the page (and *EJECT is set
673 print_binary_flush_records (struct print_trns *trns, struct string *line,
674 int target_record, bool *eject, int *record)
676 for (; target_record > *record; (*record)++)
678 char *s = ds_cstr (line);
679 size_t length = ds_length (line);
687 s[0] = recode_byte (trns->encoding, C_ENCODING, leader);
689 if (!trns->include_prefix)
694 dfm_put_record (trns->writer, s, length);
696 ds_truncate (line, 1);
702 print_trns_free (void *trns_)
704 struct print_trns *trns = trns_;
707 if (trns->writer != NULL)
708 ok = dfm_close_writer (trns->writer);
709 pool_destroy (trns->pool);
714 static const struct trns_class print_binary_trns_class = {
716 .execute = print_binary_trns_proc,
717 .destroy = print_trns_free,
720 static const struct trns_class print_text_trns_class = {
722 .execute = print_text_trns_proc,
723 .destroy = print_trns_free,