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/ll.h"
39 #include "libpspp/message.h"
40 #include "libpspp/misc.h"
41 #include "libpspp/pool.h"
42 #include "libpspp/u8-line.h"
43 #include "output/driver.h"
44 #include "output/pivot-table.h"
45 #include "output/table.h"
46 #include "output/output-item.h"
48 #include "gl/xalloc.h"
51 #define N_(msgid) msgid
52 #define _(msgid) gettext (msgid)
54 /* Describes what to do when an output field is encountered. */
57 PRT_LITERAL, /* Literal string. */
58 PRT_VAR /* Variable. */
61 /* Describes how to output one field. */
65 struct ll ll; /* In struct print_trns `specs' list. */
66 enum field_type type; /* What type of field this is. */
67 int record; /* 1-based record number. */
68 int first_column; /* 0-based first column. */
71 const struct variable *var; /* Associated variable. */
72 struct fmt_spec format; /* Output spec. */
73 bool add_space; /* Add trailing space? */
74 bool sysmis_as_spaces; /* Output SYSMIS as spaces? */
76 /* PRT_LITERAL only. */
77 struct string string; /* String to output. */
78 int width; /* Width of 'string', in display columns. */
81 /* PRINT, PRINT EJECT, WRITE private data structure. */
84 struct pool *pool; /* Stores related data. */
85 bool eject; /* Eject page before printing? */
86 bool include_prefix; /* Prefix lines with space? */
87 const char *encoding; /* Encoding to use for output. */
88 struct dfm_writer *writer; /* Output file, NULL=listing file. */
89 struct ll_list specs; /* List of struct prt_out_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 internal_cmd_print (struct lexer *, struct dataset *ds,
103 enum which_formats, bool eject);
104 static bool parse_specs (struct lexer *, struct pool *tmp_pool, struct print_trns *,
105 struct dictionary *dict, enum which_formats);
106 static void dump_table (struct print_trns *);
108 static bool print_trns_free (void *trns_);
112 /* Parses PRINT command. */
114 cmd_print (struct lexer *lexer, struct dataset *ds)
116 return internal_cmd_print (lexer, ds, PRINT, false);
119 /* Parses PRINT EJECT command. */
121 cmd_print_eject (struct lexer *lexer, struct dataset *ds)
123 return internal_cmd_print (lexer, ds, PRINT, true);
126 /* Parses WRITE command. */
128 cmd_write (struct lexer *lexer, struct dataset *ds)
130 return internal_cmd_print (lexer, ds, WRITE, false);
133 /* Parses the output commands. */
135 internal_cmd_print (struct lexer *lexer, struct dataset *ds,
136 enum which_formats which_formats, bool eject)
138 bool print_table = false;
139 const struct prt_out_spec *spec;
140 struct print_trns *trns;
141 struct file_handle *fh = NULL;
142 char *encoding = NULL;
143 struct pool *tmp_pool;
146 /* Fill in prt to facilitate error-handling. */
147 trns = pool_create_container (struct print_trns, pool);
151 ll_init (&trns->specs);
153 tmp_pool = pool_create_subpool (trns->pool);
155 /* Parse the command options. */
156 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
158 if (lex_match_id (lexer, "OUTFILE"))
160 lex_match (lexer, T_EQUALS);
162 fh = fh_parse (lexer, FH_REF_FILE, NULL);
166 else if (lex_match_id (lexer, "ENCODING"))
168 lex_match (lexer, T_EQUALS);
169 if (!lex_force_string (lexer))
173 encoding = ss_xstrdup (lex_tokss (lexer));
177 else if (lex_match_id (lexer, "RECORDS"))
179 lex_match (lexer, T_EQUALS);
180 lex_match (lexer, T_LPAREN);
181 if (!lex_force_int_range (lexer, "RECORDS", 0, INT_MAX))
183 trns->n_records = lex_integer (lexer);
185 lex_match (lexer, T_RPAREN);
187 else if (lex_match_id (lexer, "TABLE"))
189 else if (lex_match_id (lexer, "NOTABLE"))
193 lex_error_expecting (lexer, "OUTFILE", "ENCODING", "RECORDS",
199 /* When PRINT or PRINT EJECT writes to an external file, we
200 prefix each line with a space for compatibility. */
201 trns->include_prefix = which_formats == PRINT && fh != NULL;
203 /* Parse variables and strings. */
204 if (!parse_specs (lexer, tmp_pool, trns, dataset_dict (ds), which_formats))
207 /* Are there any binary formats?
209 There are real difficulties figuring out what to do when both binary
210 formats and nontrivial encodings enter the picture. So when binary
211 formats are present we fall back to much simpler handling. */
213 ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
215 if (spec->type == PRT_VAR
216 && fmt_get_category (spec->format.type) == FMT_CAT_BINARY)
222 if (binary && fh == NULL)
224 msg (SE, _("%s is required when binary formats are specified."), "OUTFILE");
228 if (lex_end_of_command (lexer) != CMD_SUCCESS)
233 trns->writer = dfm_open_writer (fh, encoding);
234 if (trns->writer == NULL)
236 trns->encoding = dfm_writer_get_encoding (trns->writer);
239 trns->encoding = UTF8;
241 /* Output the variable table if requested. */
245 /* Put the transformation in the queue. */
246 add_transformation (ds, (binary
247 ? &print_binary_trns_class
248 : &print_text_trns_class), trns);
250 pool_destroy (tmp_pool);
256 print_trns_free (trns);
261 static bool parse_string_argument (struct lexer *, struct print_trns *,
262 int record, int *column);
263 static bool parse_variable_argument (struct lexer *, const struct dictionary *,
265 struct pool *tmp_pool,
266 int *record, int *column,
269 /* Parses all the variable and string specifications on a single
270 PRINT, PRINT EJECT, or WRITE command into the prt structure.
273 parse_specs (struct lexer *lexer, struct pool *tmp_pool, struct print_trns *trns,
274 struct dictionary *dict,
275 enum which_formats which_formats)
280 if (lex_token (lexer) == T_ENDCMD)
286 while (lex_token (lexer) != T_ENDCMD)
290 if (!parse_record_placement (lexer, &record, &column))
293 if (lex_is_string (lexer))
294 ok = parse_string_argument (lexer, trns, record, &column);
296 ok = parse_variable_argument (lexer, dict, trns, tmp_pool, &record,
297 &column, which_formats);
301 lex_match (lexer, T_COMMA);
304 if (trns->n_records != 0 && trns->n_records != record)
305 msg (SW, _("Output calls for %d records but %zu specified on RECORDS "
307 record, trns->n_records);
308 trns->n_records = record;
313 /* Parses a string argument to the PRINT commands. Returns success. */
315 parse_string_argument (struct lexer *lexer, struct print_trns *trns, int record, int *column)
317 struct prt_out_spec *spec = pool_alloc (trns->pool, sizeof *spec);
318 spec->type = PRT_LITERAL;
319 spec->record = record;
320 spec->first_column = *column;
321 ds_init_substring (&spec->string, lex_tokss (lexer));
322 ds_register_pool (&spec->string, trns->pool);
325 /* Parse the included column range. */
326 if (lex_is_number (lexer))
328 int first_column, last_column;
329 bool range_specified;
331 if (!parse_column_range (lexer, 1,
332 &first_column, &last_column, &range_specified))
335 spec->first_column = first_column;
337 ds_set_length (&spec->string, last_column - first_column + 1, ' ');
340 spec->width = u8_strwidth (CHAR_CAST (const uint8_t *,
341 ds_cstr (&spec->string)),
343 *column = spec->first_column + spec->width;
345 ll_push_tail (&trns->specs, &spec->ll);
349 /* Parses a variable argument to the PRINT commands by passing it off
350 to fixed_parse_compatible() or fixed_parse_fortran() as appropriate.
353 parse_variable_argument (struct lexer *lexer, const struct dictionary *dict,
354 struct print_trns *trns, struct pool *tmp_pool,
355 int *record, int *column,
356 enum which_formats which_formats)
358 const struct variable **vars;
360 if (!parse_variables_const_pool (lexer, tmp_pool, dict,
361 &vars, &n_vars, PV_DUPLICATE))
364 struct fmt_spec *formats, *f;
367 int formats_start = lex_ofs (lexer);
368 if (lex_is_number (lexer) || lex_token (lexer) == T_LPAREN)
370 if (!parse_var_placements (lexer, tmp_pool, n_vars, FMT_FOR_OUTPUT,
371 &formats, &n_formats))
377 lex_match (lexer, T_ASTERISK);
379 formats = pool_nmalloc (tmp_pool, n_vars, sizeof *formats);
381 for (size_t i = 0; i < n_vars; i++)
383 const struct variable *v = vars[i];
384 formats[i] = (which_formats == PRINT
385 ? *var_get_print_format (v)
386 : *var_get_write_format (v));
388 add_space = which_formats == PRINT;
390 int formats_end = lex_ofs (lexer) - 1;
393 for (f = formats; f < &formats[n_formats]; f++)
394 if (!execute_placement_format (f, record, column))
396 const struct variable *var = vars[var_idx++];
397 char *error = fmt_check_width_compat__ (f, var_get_name (var),
398 var_get_width (var));
401 lex_ofs_error (lexer, formats_start, formats_end, "%s", error);
406 struct prt_out_spec *spec = pool_alloc (trns->pool, sizeof *spec);
407 *spec = (struct prt_out_spec) {
410 .first_column = *column,
413 .add_space = add_space,
415 /* This is a completely bizarre twist for compatibility: WRITE
416 outputs the system-missing value as a field filled with spaces,
417 instead of using the normal format that usually contains a
419 .sysmis_as_spaces = (which_formats == WRITE
420 && var_is_numeric (var)
421 && (fmt_get_category (f->type)
424 ll_push_tail (&trns->specs, &spec->ll);
426 *column += f->w + add_space;
428 assert (var_idx == n_vars);
433 /* Prints the table produced by the TABLE subcommand to the listing
436 dump_table (struct print_trns *trns)
438 struct pivot_table *table = pivot_table_create (N_("Print Summary"));
440 pivot_dimension_create (table, PIVOT_AXIS_COLUMN, N_("Attributes"),
441 N_("Record"), N_("Columns"), N_("Format"));
443 struct pivot_dimension *variables = pivot_dimension_create (
444 table, PIVOT_AXIS_ROW, N_("Variable"));
446 struct prt_out_spec *spec;
447 ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
449 if (spec->type != PRT_VAR)
452 int row = pivot_category_create_leaf (
453 variables->root, pivot_value_new_variable (spec->var));
455 pivot_table_put2 (table, 0, row,
456 pivot_value_new_integer (spec->record));
457 int last_column = spec->first_column + spec->format.w - 1;
458 pivot_table_put2 (table, 1, row, pivot_value_new_user_text_nocopy (
460 spec->first_column, last_column)));
462 char fmt_string[FMT_STRING_LEN_MAX + 1];
463 pivot_table_put2 (table, 2, row, pivot_value_new_user_text (
464 fmt_to_string (&spec->format, fmt_string), -1));
467 int row = pivot_category_create_leaf (
468 variables->root, pivot_value_new_text (N_("N of Records")));
469 pivot_table_put2 (table, 0, row,
470 pivot_value_new_integer (trns->n_records));
472 pivot_table_submit (table);
475 /* Transformation, for all-text output. */
477 static void print_text_flush_records (struct print_trns *, struct u8_line *,
479 bool *eject, int *record);
481 /* Performs the transformation inside print_trns T on case C. */
482 static enum trns_result
483 print_text_trns_proc (void *trns_, struct ccase **c,
484 casenumber case_num UNUSED)
486 struct print_trns *trns = trns_;
487 struct prt_out_spec *spec;
490 bool eject = trns->eject;
493 u8_line_init (&line);
494 ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
496 int x0 = spec->first_column;
498 print_text_flush_records (trns, &line, spec->record, &eject, &record);
500 u8_line_set_length (&line, spec->first_column);
501 if (spec->type == PRT_VAR)
503 const union value *input = case_data (*c, spec->var);
506 if (!spec->sysmis_as_spaces || input->f != SYSMIS)
512 s = data_out (input, var_get_encoding (spec->var),
513 &spec->format, settings_get_fmt_settings ());
515 width = u8_width (CHAR_CAST (const uint8_t *, s), len, UTF8);
517 u8_line_put (&line, x0, x1, s, len);
522 int n = spec->format.w;
525 memset (u8_line_reserve (&line, x0, x1, n), ' ', n);
529 *u8_line_reserve (&line, x1, x1 + 1, 1) = ' ';
533 const struct string *s = &spec->string;
535 u8_line_put (&line, x0, x0 + spec->width,
536 ds_data (s), ds_length (s));
539 print_text_flush_records (trns, &line, trns->n_records + 1,
541 u8_line_destroy (&line);
543 if (trns->writer != NULL && dfm_write_error (trns->writer))
545 return TRNS_CONTINUE;
548 /* Advance from *RECORD to TARGET_RECORD, outputting records
549 along the way. If *EJECT is true, then the first record
550 output is preceded by ejecting the page (and *EJECT is set
553 print_text_flush_records (struct print_trns *trns, struct u8_line *line,
554 int target_record, bool *eject, int *record)
556 for (; target_record > *record; (*record)++)
563 if (trns->writer == NULL)
564 output_item_submit (page_break_item_create ());
568 *u8_line_reserve (line, 0, 1, 1) = leader;
570 if (trns->writer == NULL)
571 output_log ("%s", ds_cstr (&line->s) + 1);
574 size_t len = ds_length (&line->s);
575 char *s = ds_cstr (&line->s);
577 if (!trns->include_prefix)
583 dfm_put_record_utf8 (trns->writer, s, len);
588 /* Transformation, for output involving binary. */
590 static void print_binary_flush_records (struct print_trns *,
591 struct string *line, int target_record,
592 bool *eject, int *record);
594 /* Performs the transformation inside print_trns T on case C. */
595 static enum trns_result
596 print_binary_trns_proc (void *trns_, struct ccase **c,
597 casenumber case_num UNUSED)
599 struct print_trns *trns = trns_;
600 bool eject = trns->eject;
601 char encoded_space = recode_byte (trns->encoding, C_ENCODING, ' ');
603 struct prt_out_spec *spec;
606 ds_init_empty (&line);
607 ds_put_byte (&line, ' ');
608 ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
610 print_binary_flush_records (trns, &line, spec->record, &eject, &record);
612 ds_set_length (&line, spec->first_column, encoded_space);
613 if (spec->type == PRT_VAR)
615 const union value *input = case_data (*c, spec->var);
616 if (!spec->sysmis_as_spaces || input->f != SYSMIS)
617 data_out_recode (input, var_get_encoding (spec->var),
618 &spec->format, settings_get_fmt_settings (),
619 &line, trns->encoding);
621 ds_put_byte_multiple (&line, encoded_space, spec->format.w);
623 ds_put_byte (&line, encoded_space);
627 ds_put_substring (&line, ds_ss (&spec->string));
628 if (0 != strcmp (trns->encoding, UTF8))
630 size_t length = ds_length (&spec->string);
631 char *data = ss_data (ds_tail (&line, length));
632 char *s = recode_string (trns->encoding, UTF8, data, length);
633 memcpy (data, s, length);
638 print_binary_flush_records (trns, &line, trns->n_records + 1,
642 if (trns->writer != NULL && dfm_write_error (trns->writer))
644 return TRNS_CONTINUE;
647 /* Advance from *RECORD to TARGET_RECORD, outputting records
648 along the way. If *EJECT is true, then the first record
649 output is preceded by ejecting the page (and *EJECT is set
652 print_binary_flush_records (struct print_trns *trns, struct string *line,
653 int target_record, bool *eject, int *record)
655 for (; target_record > *record; (*record)++)
657 char *s = ds_cstr (line);
658 size_t length = ds_length (line);
666 s[0] = recode_byte (trns->encoding, C_ENCODING, leader);
668 if (!trns->include_prefix)
673 dfm_put_record (trns->writer, s, length);
675 ds_truncate (line, 1);
681 print_trns_free (void *trns_)
683 struct print_trns *trns = trns_;
686 if (trns->writer != NULL)
687 ok = dfm_close_writer (trns->writer);
688 pool_destroy (trns->pool);
693 static const struct trns_class print_binary_trns_class = {
695 .execute = print_binary_trns_proc,
696 .destroy = print_trns_free,
699 static const struct trns_class print_text_trns_class = {
701 .execute = print_text_trns_proc,
702 .destroy = print_trns_free,