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_width (var)))
404 spec = pool_alloc (trns->pool, sizeof *spec);
405 spec->type = PRT_VAR;
406 spec->record = *record;
407 spec->first_column = *column;
410 spec->add_space = add_space;
412 /* This is a completely bizarre twist for compatibility:
413 WRITE outputs the system-missing value as a field
414 filled with spaces, instead of using the normal format
415 that usually contains a period. */
416 spec->sysmis_as_spaces = (which_formats == WRITE
417 && var_is_numeric (var)
418 && (fmt_get_category (spec->format.type)
421 ll_push_tail (&trns->specs, &spec->ll);
423 *column += f->w + add_space;
425 assert (var_idx == n_vars);
430 /* Prints the table produced by the TABLE subcommand to the listing
433 dump_table (struct print_trns *trns)
435 struct pivot_table *table = pivot_table_create (N_("Print Summary"));
437 pivot_dimension_create (table, PIVOT_AXIS_COLUMN, N_("Attributes"),
438 N_("Record"), N_("Columns"), N_("Format"));
440 struct pivot_dimension *variables = pivot_dimension_create (
441 table, PIVOT_AXIS_ROW, N_("Variable"));
443 struct prt_out_spec *spec;
444 ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
446 if (spec->type != PRT_VAR)
449 int row = pivot_category_create_leaf (
450 variables->root, pivot_value_new_variable (spec->var));
452 pivot_table_put2 (table, 0, row,
453 pivot_value_new_integer (spec->record));
454 int last_column = spec->first_column + spec->format.w - 1;
455 pivot_table_put2 (table, 1, row, pivot_value_new_user_text_nocopy (
457 spec->first_column, last_column)));
459 char fmt_string[FMT_STRING_LEN_MAX + 1];
460 pivot_table_put2 (table, 2, row, pivot_value_new_user_text (
461 fmt_to_string (&spec->format, fmt_string), -1));
464 int row = pivot_category_create_leaf (
465 variables->root, pivot_value_new_text (N_("N of Records")));
466 pivot_table_put2 (table, 0, row,
467 pivot_value_new_integer (trns->n_records));
469 pivot_table_submit (table);
472 /* Transformation, for all-text output. */
474 static void print_text_flush_records (struct print_trns *, struct u8_line *,
476 bool *eject, int *record);
478 /* Performs the transformation inside print_trns T on case C. */
479 static enum trns_result
480 print_text_trns_proc (void *trns_, struct ccase **c,
481 casenumber case_num UNUSED)
483 struct print_trns *trns = trns_;
484 struct prt_out_spec *spec;
487 bool eject = trns->eject;
490 u8_line_init (&line);
491 ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
493 int x0 = spec->first_column;
495 print_text_flush_records (trns, &line, spec->record, &eject, &record);
497 u8_line_set_length (&line, spec->first_column);
498 if (spec->type == PRT_VAR)
500 const union value *input = case_data (*c, spec->var);
503 if (!spec->sysmis_as_spaces || input->f != SYSMIS)
509 s = data_out (input, var_get_encoding (spec->var),
510 &spec->format, settings_get_fmt_settings ());
512 width = u8_width (CHAR_CAST (const uint8_t *, s), len, UTF8);
514 u8_line_put (&line, x0, x1, s, len);
519 int n = spec->format.w;
522 memset (u8_line_reserve (&line, x0, x1, n), ' ', n);
526 *u8_line_reserve (&line, x1, x1 + 1, 1) = ' ';
530 const struct string *s = &spec->string;
532 u8_line_put (&line, x0, x0 + spec->width,
533 ds_data (s), ds_length (s));
536 print_text_flush_records (trns, &line, trns->n_records + 1,
538 u8_line_destroy (&line);
540 if (trns->writer != NULL && dfm_write_error (trns->writer))
542 return TRNS_CONTINUE;
545 /* Advance from *RECORD to TARGET_RECORD, outputting records
546 along the way. If *EJECT is true, then the first record
547 output is preceded by ejecting the page (and *EJECT is set
550 print_text_flush_records (struct print_trns *trns, struct u8_line *line,
551 int target_record, bool *eject, int *record)
553 for (; target_record > *record; (*record)++)
560 if (trns->writer == NULL)
561 output_item_submit (page_break_item_create ());
565 *u8_line_reserve (line, 0, 1, 1) = leader;
567 if (trns->writer == NULL)
568 output_log ("%s", ds_cstr (&line->s) + 1);
571 size_t len = ds_length (&line->s);
572 char *s = ds_cstr (&line->s);
574 if (!trns->include_prefix)
580 dfm_put_record_utf8 (trns->writer, s, len);
585 /* Transformation, for output involving binary. */
587 static void print_binary_flush_records (struct print_trns *,
588 struct string *line, int target_record,
589 bool *eject, int *record);
591 /* Performs the transformation inside print_trns T on case C. */
592 static enum trns_result
593 print_binary_trns_proc (void *trns_, struct ccase **c,
594 casenumber case_num UNUSED)
596 struct print_trns *trns = trns_;
597 bool eject = trns->eject;
598 char encoded_space = recode_byte (trns->encoding, C_ENCODING, ' ');
600 struct prt_out_spec *spec;
603 ds_init_empty (&line);
604 ds_put_byte (&line, ' ');
605 ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
607 print_binary_flush_records (trns, &line, spec->record, &eject, &record);
609 ds_set_length (&line, spec->first_column, encoded_space);
610 if (spec->type == PRT_VAR)
612 const union value *input = case_data (*c, spec->var);
613 if (!spec->sysmis_as_spaces || input->f != SYSMIS)
614 data_out_recode (input, var_get_encoding (spec->var),
615 &spec->format, settings_get_fmt_settings (),
616 &line, trns->encoding);
618 ds_put_byte_multiple (&line, encoded_space, spec->format.w);
620 ds_put_byte (&line, encoded_space);
624 ds_put_substring (&line, ds_ss (&spec->string));
625 if (0 != strcmp (trns->encoding, UTF8))
627 size_t length = ds_length (&spec->string);
628 char *data = ss_data (ds_tail (&line, length));
629 char *s = recode_string (trns->encoding, UTF8, data, length);
630 memcpy (data, s, length);
635 print_binary_flush_records (trns, &line, trns->n_records + 1,
639 if (trns->writer != NULL && dfm_write_error (trns->writer))
641 return TRNS_CONTINUE;
644 /* Advance from *RECORD to TARGET_RECORD, outputting records
645 along the way. If *EJECT is true, then the first record
646 output is preceded by ejecting the page (and *EJECT is set
649 print_binary_flush_records (struct print_trns *trns, struct string *line,
650 int target_record, bool *eject, int *record)
652 for (; target_record > *record; (*record)++)
654 char *s = ds_cstr (line);
655 size_t length = ds_length (line);
663 s[0] = recode_byte (trns->encoding, C_ENCODING, leader);
665 if (!trns->include_prefix)
670 dfm_put_record (trns->writer, s, length);
672 ds_truncate (line, 1);
678 print_trns_free (void *trns_)
680 struct print_trns *trns = trns_;
683 if (trns->writer != NULL)
684 ok = dfm_close_writer (trns->writer);
685 pool_destroy (trns->pool);
690 static const struct trns_class print_binary_trns_class = {
692 .execute = print_binary_trns_proc,
693 .destroy = print_trns_free,
696 static const struct trns_class print_text_trns_class = {
698 .execute = print_text_trns_proc,
699 .destroy = print_trns_free,