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/data-io/data-writer.h"
30 #include "language/data-io/file-handle.h"
31 #include "language/data-io/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/table.h"
45 #include "output/output-item.h"
47 #include "gl/xalloc.h"
50 #define N_(msgid) msgid
51 #define _(msgid) gettext (msgid)
53 /* Describes what to do when an output field is encountered. */
56 PRT_LITERAL, /* Literal string. */
57 PRT_VAR /* Variable. */
60 /* Describes how to output one field. */
64 enum field_type type; /* What type of field this is. */
65 int record; /* 1-based record number. */
66 int first_column; /* 0-based first column. */
67 int start_ofs, end_ofs;
70 const struct variable *var; /* Associated variable. */
71 struct fmt_spec format; /* Output spec. */
72 bool add_space; /* Add trailing space? */
73 bool sysmis_as_spaces; /* Output SYSMIS as spaces? */
75 /* PRT_LITERAL only. */
76 struct substring string; /* String to output. */
77 int width; /* Width of 'string', in display columns. */
80 /* PRINT, PRINT EJECT, WRITE private data structure. */
83 struct pool *pool; /* Stores related data. */
84 bool eject; /* Eject page before printing? */
85 bool include_prefix; /* Prefix lines with space? */
86 const char *encoding; /* Encoding to use for output. */
87 struct dfm_writer *writer; /* Output file, NULL=listing file. */
88 struct prt_out_spec *specs;
90 size_t n_records; /* Number of records to write. */
99 static const struct trns_class print_binary_trns_class;
100 static const struct trns_class print_text_trns_class;
102 static int cmd_print__ (struct lexer *, struct dataset *,
103 enum which_formats, bool eject);
104 static bool parse_specs (struct lexer *, struct pool *tmp_pool,
105 struct print_trns *, int records_ofs,
106 struct dictionary *, enum which_formats);
107 static void dump_table (struct print_trns *);
109 static bool print_trns_free (void *trns_);
111 static const struct prt_out_spec *find_binary_spec (const struct print_trns *);
115 /* Parses PRINT command. */
117 cmd_print (struct lexer *lexer, struct dataset *ds)
119 return cmd_print__ (lexer, ds, PRINT, false);
122 /* Parses PRINT EJECT command. */
124 cmd_print_eject (struct lexer *lexer, struct dataset *ds)
126 return cmd_print__ (lexer, ds, PRINT, true);
129 /* Parses WRITE command. */
131 cmd_write (struct lexer *lexer, struct dataset *ds)
133 return cmd_print__ (lexer, ds, WRITE, false);
136 /* Parses the output commands. */
138 cmd_print__ (struct lexer *lexer, struct dataset *ds,
139 enum which_formats which_formats, bool eject)
141 bool print_table = false;
142 struct file_handle *fh = NULL;
143 char *encoding = NULL;
145 /* Fill in prt to facilitate error-handling. */
146 struct pool *pool = pool_create ();
147 struct print_trns *trns = pool_alloc (pool, sizeof *trns);
148 *trns = (struct print_trns) { .pool = pool, .eject = eject };
149 struct pool *tmp_pool = pool_create_subpool (trns->pool);
151 /* Parse the command options. */
153 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
155 if (lex_match_id (lexer, "OUTFILE"))
157 lex_match (lexer, T_EQUALS);
159 fh = fh_parse (lexer, FH_REF_FILE, NULL);
163 else if (lex_match_id (lexer, "ENCODING"))
165 lex_match (lexer, T_EQUALS);
166 if (!lex_force_string (lexer))
170 encoding = ss_xstrdup (lex_tokss (lexer));
174 else if (lex_match_id (lexer, "RECORDS"))
176 lex_match (lexer, T_EQUALS);
177 lex_match (lexer, T_LPAREN);
178 if (!lex_force_int_range (lexer, "RECORDS", 0, INT_MAX))
180 trns->n_records = lex_integer (lexer);
181 records_ofs = lex_ofs (lexer);
183 lex_match (lexer, T_RPAREN);
185 else if (lex_match_id (lexer, "TABLE"))
187 else if (lex_match_id (lexer, "NOTABLE"))
191 lex_error_expecting (lexer, "OUTFILE", "ENCODING", "RECORDS",
197 /* When PRINT or PRINT EJECT writes to an external file, we
198 prefix each line with a space for compatibility. */
199 trns->include_prefix = which_formats == PRINT && fh != NULL;
201 /* Parse variables and strings. */
202 if (!parse_specs (lexer, tmp_pool, trns, records_ofs,
203 dataset_dict (ds), which_formats))
206 /* Are there any binary formats?
208 There are real difficulties figuring out what to do when both binary
209 formats and nontrivial encodings enter the picture. So when binary
210 formats are present we fall back to much simpler handling. */
211 const struct prt_out_spec *binary_spec = find_binary_spec (trns);
212 if (binary_spec && !fh)
214 lex_ofs_error (lexer, binary_spec->start_ofs, binary_spec->end_ofs,
215 _("%s is required when binary formats are specified."),
220 if (lex_end_of_command (lexer) != CMD_SUCCESS)
225 trns->writer = dfm_open_writer (fh, encoding);
226 if (trns->writer == NULL)
228 trns->encoding = dfm_writer_get_encoding (trns->writer);
231 trns->encoding = UTF8;
233 /* Output the variable table if requested. */
237 /* Put the transformation in the queue. */
238 add_transformation (ds, (binary_spec
239 ? &print_binary_trns_class
240 : &print_text_trns_class), trns);
242 pool_destroy (tmp_pool);
248 print_trns_free (trns);
253 static bool parse_string_argument (struct lexer *, struct print_trns *,
254 size_t *allocated_specs,
255 int record, int *column);
256 static bool parse_variable_argument (struct lexer *, const struct dictionary *,
258 size_t *allocated_specs,
259 struct pool *tmp_pool,
260 int *record, int *column,
263 /* Parses all the variable and string specifications on a single
264 PRINT, PRINT EJECT, or WRITE command into the prt structure.
267 parse_specs (struct lexer *lexer, struct pool *tmp_pool,
268 struct print_trns *trns, int records_ofs, struct dictionary *dict,
269 enum which_formats which_formats)
274 if (lex_token (lexer) == T_ENDCMD)
280 size_t allocated_specs = 0;
281 while (lex_token (lexer) != T_ENDCMD)
283 if (!parse_record_placement (lexer, &record, &column))
286 bool ok = (lex_is_string (lexer)
287 ? parse_string_argument (lexer, trns, &allocated_specs,
289 : parse_variable_argument (lexer, dict, trns, &allocated_specs,
290 tmp_pool, &record, &column,
295 lex_match (lexer, T_COMMA);
298 if (trns->n_records != 0 && trns->n_records != record)
299 lex_ofs_error (lexer, records_ofs, records_ofs,
300 _("Output calls for %d records but %zu specified on RECORDS "
302 record, trns->n_records);
303 trns->n_records = record;
308 static struct prt_out_spec *
309 add_spec (struct print_trns *trns, size_t *allocated_specs)
311 if (trns->n_specs >= *allocated_specs)
312 trns->specs = pool_2nrealloc (trns->pool, trns->specs, allocated_specs,
313 sizeof *trns->specs);
314 return &trns->specs[trns->n_specs++];
317 /* Parses a string argument to the PRINT commands. Returns success. */
319 parse_string_argument (struct lexer *lexer, struct print_trns *trns,
320 size_t *allocated_specs, int record, int *column)
322 struct prt_out_spec *spec = add_spec (trns, allocated_specs);
323 *spec = (struct prt_out_spec) {
326 .first_column = *column,
327 .string = ss_clone_pool (lex_tokss (lexer), trns->pool),
328 .start_ofs = lex_ofs (lexer),
332 /* Parse the included column range. */
333 if (lex_is_number (lexer))
335 int first_column, last_column;
336 bool range_specified;
338 if (!parse_column_range (lexer, 1,
339 &first_column, &last_column, &range_specified))
342 spec->first_column = first_column;
346 ds_init_substring (&s, spec->string);
347 ds_set_length (&s, last_column - first_column + 1, ' ');
348 spec->string = ss_clone_pool (s.ss, trns->pool);
352 spec->end_ofs = lex_ofs (lexer) - 1;
354 spec->width = u8_width (CHAR_CAST (const uint8_t *, spec->string.string),
355 spec->string.length, UTF8);
356 *column = spec->first_column + spec->width;
361 /* Parses a variable argument to the PRINT commands by passing it off
362 to fixed_parse_compatible() or fixed_parse_fortran() as appropriate.
365 parse_variable_argument (struct lexer *lexer, const struct dictionary *dict,
366 struct print_trns *trns, size_t *allocated_specs,
367 struct pool *tmp_pool, int *record, int *column,
368 enum which_formats which_formats)
370 const struct variable **vars;
372 if (!parse_variables_const_pool (lexer, tmp_pool, dict,
373 &vars, &n_vars, PV_DUPLICATE))
376 struct fmt_spec *formats, *f;
379 int formats_start = lex_ofs (lexer);
380 if (lex_is_number (lexer) || lex_token (lexer) == T_LPAREN)
382 if (!parse_var_placements (lexer, tmp_pool, n_vars, FMT_FOR_OUTPUT,
383 &formats, &n_formats))
389 lex_match (lexer, T_ASTERISK);
391 formats = pool_nmalloc (tmp_pool, n_vars, sizeof *formats);
393 for (size_t i = 0; i < n_vars; i++)
395 const struct variable *v = vars[i];
396 formats[i] = (which_formats == PRINT
397 ? *var_get_print_format (v)
398 : *var_get_write_format (v));
400 add_space = which_formats == PRINT;
402 int formats_end = lex_ofs (lexer) - 1;
405 for (f = formats; f < &formats[n_formats]; f++)
406 if (!execute_placement_format (f, record, column))
408 const struct variable *var = vars[var_idx++];
409 char *error = fmt_check_width_compat__ (f, var_get_name (var),
410 var_get_width (var));
413 lex_ofs_error (lexer, formats_start, formats_end, "%s", error);
418 struct prt_out_spec *spec = add_spec (trns, allocated_specs);
419 *spec = (struct prt_out_spec) {
422 .first_column = *column,
425 .add_space = add_space,
427 /* This is a completely bizarre twist for compatibility: WRITE
428 outputs the system-missing value as a field filled with spaces,
429 instead of using the normal format that usually contains a
431 .sysmis_as_spaces = (which_formats == WRITE
432 && var_is_numeric (var)
433 && (fmt_get_category (f->type)
437 *column += f->w + add_space;
439 assert (var_idx == n_vars);
444 /* Prints the table produced by the TABLE subcommand to the listing
447 dump_table (struct print_trns *trns)
449 struct pivot_table *table = pivot_table_create (N_("Print Summary"));
451 pivot_dimension_create (table, PIVOT_AXIS_COLUMN, N_("Attributes"),
452 N_("Record"), N_("Columns"), N_("Format"));
454 struct pivot_dimension *variables = pivot_dimension_create (
455 table, PIVOT_AXIS_ROW, N_("Variable"));
457 for (size_t i = 0; i < trns->n_specs; i++)
459 const struct prt_out_spec *spec = &trns->specs[i];
460 if (spec->type != PRT_VAR)
463 int row = pivot_category_create_leaf (
464 variables->root, pivot_value_new_variable (spec->var));
466 pivot_table_put2 (table, 0, row,
467 pivot_value_new_integer (spec->record));
468 int last_column = spec->first_column + spec->format.w - 1;
469 pivot_table_put2 (table, 1, row, pivot_value_new_user_text_nocopy (
471 spec->first_column, last_column)));
473 char fmt_string[FMT_STRING_LEN_MAX + 1];
474 pivot_table_put2 (table, 2, row, pivot_value_new_user_text (
475 fmt_to_string (&spec->format, fmt_string), -1));
478 int row = pivot_category_create_leaf (
479 variables->root, pivot_value_new_text (N_("N of Records")));
480 pivot_table_put2 (table, 0, row,
481 pivot_value_new_integer (trns->n_records));
483 pivot_table_submit (table);
486 static const struct prt_out_spec *
487 find_binary_spec (const struct print_trns *trns)
489 for (size_t i = 0; i < trns->n_specs; i++)
491 const struct prt_out_spec *spec = &trns->specs[i];
492 if (spec->type == PRT_VAR
493 && fmt_get_category (spec->format.type) == FMT_CAT_BINARY)
499 /* Transformation, for all-text output. */
501 static void print_text_flush_records (struct print_trns *, struct u8_line *,
503 bool *eject, int *record);
505 /* Performs the transformation inside print_trns T on case C. */
506 static enum trns_result
507 print_text_trns_proc (void *trns_, struct ccase **c,
508 casenumber case_num UNUSED)
510 struct print_trns *trns = trns_;
513 bool eject = trns->eject;
516 u8_line_init (&line);
517 for (size_t i = 0; i < trns->n_specs; i++)
519 const struct prt_out_spec *spec = &trns->specs[i];
520 int x0 = spec->first_column;
522 print_text_flush_records (trns, &line, spec->record, &eject, &record);
524 u8_line_set_length (&line, spec->first_column);
525 if (spec->type == PRT_VAR)
527 const union value *input = case_data (*c, spec->var);
530 if (!spec->sysmis_as_spaces || input->f != SYSMIS)
536 s = data_out (input, var_get_encoding (spec->var),
537 &spec->format, settings_get_fmt_settings ());
539 width = u8_width (CHAR_CAST (const uint8_t *, s), len, UTF8);
541 u8_line_put (&line, x0, x1, s, len);
546 int n = spec->format.w;
549 memset (u8_line_reserve (&line, x0, x1, n), ' ', n);
553 *u8_line_reserve (&line, x1, x1 + 1, 1) = ' ';
557 const struct substring *s = &spec->string;
559 u8_line_put (&line, x0, x0 + spec->width, s->string, s->length);
562 print_text_flush_records (trns, &line, trns->n_records + 1,
564 u8_line_destroy (&line);
566 if (trns->writer != NULL && dfm_write_error (trns->writer))
568 return TRNS_CONTINUE;
571 /* Advance from *RECORD to TARGET_RECORD, outputting records
572 along the way. If *EJECT is true, then the first record
573 output is preceded by ejecting the page (and *EJECT is set
576 print_text_flush_records (struct print_trns *trns, struct u8_line *line,
577 int target_record, bool *eject, int *record)
579 for (; target_record > *record; (*record)++)
586 if (trns->writer == NULL)
587 output_item_submit (page_break_item_create ());
591 *u8_line_reserve (line, 0, 1, 1) = leader;
593 if (trns->writer == NULL)
594 output_log ("%s", ds_cstr (&line->s) + 1);
597 size_t len = ds_length (&line->s);
598 char *s = ds_cstr (&line->s);
600 if (!trns->include_prefix)
606 dfm_put_record_utf8 (trns->writer, s, len);
611 /* Transformation, for output involving binary. */
613 static void print_binary_flush_records (struct print_trns *,
614 struct string *line, int target_record,
615 bool *eject, int *record);
617 /* Performs the transformation inside print_trns T on case C. */
618 static enum trns_result
619 print_binary_trns_proc (void *trns_, struct ccase **c,
620 casenumber case_num UNUSED)
622 struct print_trns *trns = trns_;
623 bool eject = trns->eject;
624 char encoded_space = recode_byte (trns->encoding, C_ENCODING, ' ');
626 struct string line = DS_EMPTY_INITIALIZER;
628 ds_put_byte (&line, ' ');
629 for (size_t i = 0; i < trns->n_specs; i++)
631 const struct prt_out_spec *spec = &trns->specs[i];
632 print_binary_flush_records (trns, &line, spec->record, &eject, &record);
634 ds_set_length (&line, spec->first_column, encoded_space);
635 if (spec->type == PRT_VAR)
637 const union value *input = case_data (*c, spec->var);
638 if (!spec->sysmis_as_spaces || input->f != SYSMIS)
639 data_out_recode (input, var_get_encoding (spec->var),
640 &spec->format, settings_get_fmt_settings (),
641 &line, trns->encoding);
643 ds_put_byte_multiple (&line, encoded_space, spec->format.w);
645 ds_put_byte (&line, encoded_space);
649 ds_put_substring (&line, spec->string);
650 if (0 != strcmp (trns->encoding, UTF8))
652 size_t length = spec->string.length;
653 char *data = ss_data (ds_tail (&line, length));
654 char *s = recode_string (trns->encoding, UTF8, data, length);
655 memcpy (data, s, length);
660 print_binary_flush_records (trns, &line, trns->n_records + 1,
664 if (trns->writer != NULL && dfm_write_error (trns->writer))
666 return TRNS_CONTINUE;
669 /* Advance from *RECORD to TARGET_RECORD, outputting records
670 along the way. If *EJECT is true, then the first record
671 output is preceded by ejecting the page (and *EJECT is set
674 print_binary_flush_records (struct print_trns *trns, struct string *line,
675 int target_record, bool *eject, int *record)
677 for (; target_record > *record; (*record)++)
679 char *s = ds_cstr (line);
680 size_t length = ds_length (line);
688 s[0] = recode_byte (trns->encoding, C_ENCODING, leader);
690 if (!trns->include_prefix)
695 dfm_put_record (trns->writer, s, length);
697 ds_truncate (line, 1);
703 print_trns_free (void *trns_)
705 struct print_trns *trns = trns_;
708 if (trns->writer != NULL)
709 ok = dfm_close_writer (trns->writer);
710 pool_destroy (trns->pool);
715 static const struct trns_class print_binary_trns_class = {
717 .execute = print_binary_trns_proc,
718 .destroy = print_trns_free,
721 static const struct trns_class print_text_trns_class = {
723 .execute = print_text_trns_proc,
724 .destroy = print_trns_free,