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;
359 size_t n_vars, var_idx;
360 struct fmt_spec *formats, *f;
364 if (!parse_variables_const_pool (lexer, tmp_pool, dict,
365 &vars, &n_vars, PV_DUPLICATE))
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))
379 lex_match (lexer, T_ASTERISK);
381 formats = pool_nmalloc (tmp_pool, n_vars, sizeof *formats);
383 for (i = 0; i < n_vars; i++)
385 const struct variable *v = vars[i];
386 formats[i] = (which_formats == PRINT
387 ? *var_get_print_format (v)
388 : *var_get_write_format (v));
390 add_space = which_formats == PRINT;
394 for (f = formats; f < &formats[n_formats]; f++)
395 if (!execute_placement_format (f, record, column))
397 const struct variable *var;
398 struct prt_out_spec *spec;
400 var = vars[var_idx++];
401 if (!fmt_check_width_compat (f, var_get_name (var),
402 var_get_width (var)))
405 spec = pool_alloc (trns->pool, sizeof *spec);
406 spec->type = PRT_VAR;
407 spec->record = *record;
408 spec->first_column = *column;
411 spec->add_space = add_space;
413 /* This is a completely bizarre twist for compatibility:
414 WRITE outputs the system-missing value as a field
415 filled with spaces, instead of using the normal format
416 that usually contains a period. */
417 spec->sysmis_as_spaces = (which_formats == WRITE
418 && var_is_numeric (var)
419 && (fmt_get_category (spec->format.type)
422 ll_push_tail (&trns->specs, &spec->ll);
424 *column += f->w + add_space;
426 assert (var_idx == n_vars);
431 /* Prints the table produced by the TABLE subcommand to the listing
434 dump_table (struct print_trns *trns)
436 struct pivot_table *table = pivot_table_create (N_("Print Summary"));
438 pivot_dimension_create (table, PIVOT_AXIS_COLUMN, N_("Attributes"),
439 N_("Record"), N_("Columns"), N_("Format"));
441 struct pivot_dimension *variables = pivot_dimension_create (
442 table, PIVOT_AXIS_ROW, N_("Variable"));
444 struct prt_out_spec *spec;
445 ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
447 if (spec->type != PRT_VAR)
450 int row = pivot_category_create_leaf (
451 variables->root, pivot_value_new_variable (spec->var));
453 pivot_table_put2 (table, 0, row,
454 pivot_value_new_integer (spec->record));
455 int last_column = spec->first_column + spec->format.w - 1;
456 pivot_table_put2 (table, 1, row, pivot_value_new_user_text_nocopy (
458 spec->first_column, last_column)));
460 char fmt_string[FMT_STRING_LEN_MAX + 1];
461 pivot_table_put2 (table, 2, row, pivot_value_new_user_text (
462 fmt_to_string (&spec->format, fmt_string), -1));
465 int row = pivot_category_create_leaf (
466 variables->root, pivot_value_new_text (N_("N of Records")));
467 pivot_table_put2 (table, 0, row,
468 pivot_value_new_integer (trns->n_records));
470 pivot_table_submit (table);
473 /* Transformation, for all-text output. */
475 static void print_text_flush_records (struct print_trns *, struct u8_line *,
477 bool *eject, int *record);
479 /* Performs the transformation inside print_trns T on case C. */
480 static enum trns_result
481 print_text_trns_proc (void *trns_, struct ccase **c,
482 casenumber case_num UNUSED)
484 struct print_trns *trns = trns_;
485 struct prt_out_spec *spec;
488 bool eject = trns->eject;
491 u8_line_init (&line);
492 ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
494 int x0 = spec->first_column;
496 print_text_flush_records (trns, &line, spec->record, &eject, &record);
498 u8_line_set_length (&line, spec->first_column);
499 if (spec->type == PRT_VAR)
501 const union value *input = case_data (*c, spec->var);
504 if (!spec->sysmis_as_spaces || input->f != SYSMIS)
510 s = data_out (input, var_get_encoding (spec->var),
511 &spec->format, settings_get_fmt_settings ());
513 width = u8_width (CHAR_CAST (const uint8_t *, s), len, UTF8);
515 u8_line_put (&line, x0, x1, s, len);
520 int n = spec->format.w;
523 memset (u8_line_reserve (&line, x0, x1, n), ' ', n);
527 *u8_line_reserve (&line, x1, x1 + 1, 1) = ' ';
531 const struct string *s = &spec->string;
533 u8_line_put (&line, x0, x0 + spec->width,
534 ds_data (s), ds_length (s));
537 print_text_flush_records (trns, &line, trns->n_records + 1,
539 u8_line_destroy (&line);
541 if (trns->writer != NULL && dfm_write_error (trns->writer))
543 return TRNS_CONTINUE;
546 /* Advance from *RECORD to TARGET_RECORD, outputting records
547 along the way. If *EJECT is true, then the first record
548 output is preceded by ejecting the page (and *EJECT is set
551 print_text_flush_records (struct print_trns *trns, struct u8_line *line,
552 int target_record, bool *eject, int *record)
554 for (; target_record > *record; (*record)++)
561 if (trns->writer == NULL)
562 output_item_submit (page_break_item_create ());
566 *u8_line_reserve (line, 0, 1, 1) = leader;
568 if (trns->writer == NULL)
569 output_log ("%s", ds_cstr (&line->s) + 1);
572 size_t len = ds_length (&line->s);
573 char *s = ds_cstr (&line->s);
575 if (!trns->include_prefix)
581 dfm_put_record_utf8 (trns->writer, s, len);
586 /* Transformation, for output involving binary. */
588 static void print_binary_flush_records (struct print_trns *,
589 struct string *line, int target_record,
590 bool *eject, int *record);
592 /* Performs the transformation inside print_trns T on case C. */
593 static enum trns_result
594 print_binary_trns_proc (void *trns_, struct ccase **c,
595 casenumber case_num UNUSED)
597 struct print_trns *trns = trns_;
598 bool eject = trns->eject;
599 char encoded_space = recode_byte (trns->encoding, C_ENCODING, ' ');
601 struct prt_out_spec *spec;
604 ds_init_empty (&line);
605 ds_put_byte (&line, ' ');
606 ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
608 print_binary_flush_records (trns, &line, spec->record, &eject, &record);
610 ds_set_length (&line, spec->first_column, encoded_space);
611 if (spec->type == PRT_VAR)
613 const union value *input = case_data (*c, spec->var);
614 if (!spec->sysmis_as_spaces || input->f != SYSMIS)
615 data_out_recode (input, var_get_encoding (spec->var),
616 &spec->format, settings_get_fmt_settings (),
617 &line, trns->encoding);
619 ds_put_byte_multiple (&line, encoded_space, spec->format.w);
621 ds_put_byte (&line, encoded_space);
625 ds_put_substring (&line, ds_ss (&spec->string));
626 if (0 != strcmp (trns->encoding, UTF8))
628 size_t length = ds_length (&spec->string);
629 char *data = ss_data (ds_tail (&line, length));
630 char *s = recode_string (trns->encoding, UTF8, data, length);
631 memcpy (data, s, length);
636 print_binary_flush_records (trns, &line, trns->n_records + 1,
640 if (trns->writer != NULL && dfm_write_error (trns->writer))
642 return TRNS_CONTINUE;
645 /* Advance from *RECORD to TARGET_RECORD, outputting records
646 along the way. If *EJECT is true, then the first record
647 output is preceded by ejecting the page (and *EJECT is set
650 print_binary_flush_records (struct print_trns *trns, struct string *line,
651 int target_record, bool *eject, int *record)
653 for (; target_record > *record; (*record)++)
655 char *s = ds_cstr (line);
656 size_t length = ds_length (line);
664 s[0] = recode_byte (trns->encoding, C_ENCODING, leader);
666 if (!trns->include_prefix)
671 dfm_put_record (trns->writer, s, length);
673 ds_truncate (line, 1);
679 print_trns_free (void *trns_)
681 struct print_trns *trns = trns_;
684 if (trns->writer != NULL)
685 ok = dfm_close_writer (trns->writer);
686 pool_destroy (trns->pool);
691 static const struct trns_class print_binary_trns_class = {
693 .execute = print_binary_trns_proc,
694 .destroy = print_trns_free,
697 static const struct trns_class print_text_trns_class = {
699 .execute = print_text_trns_proc,
700 .destroy = print_trns_free,