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 (lexer, _("expecting a valid subcommand"));
198 /* When PRINT or PRINT EJECT writes to an external file, we
199 prefix each line with a space for compatibility. */
200 trns->include_prefix = which_formats == PRINT && fh != NULL;
202 /* Parse variables and strings. */
203 if (!parse_specs (lexer, tmp_pool, trns, 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. */
212 ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
214 if (spec->type == PRT_VAR
215 && fmt_get_category (spec->format.type) == FMT_CAT_BINARY)
221 if (binary && fh == NULL)
223 msg (SE, _("%s is required when binary formats are specified."), "OUTFILE");
227 if (lex_end_of_command (lexer) != CMD_SUCCESS)
232 trns->writer = dfm_open_writer (fh, encoding);
233 if (trns->writer == NULL)
235 trns->encoding = dfm_writer_get_encoding (trns->writer);
238 trns->encoding = UTF8;
240 /* Output the variable table if requested. */
244 /* Put the transformation in the queue. */
245 add_transformation (ds, (binary
246 ? &print_binary_trns_class
247 : &print_text_trns_class), trns);
249 pool_destroy (tmp_pool);
255 print_trns_free (trns);
260 static bool parse_string_argument (struct lexer *, struct print_trns *,
261 int record, int *column);
262 static bool parse_variable_argument (struct lexer *, const struct dictionary *,
264 struct pool *tmp_pool,
265 int *record, int *column,
268 /* Parses all the variable and string specifications on a single
269 PRINT, PRINT EJECT, or WRITE command into the prt structure.
272 parse_specs (struct lexer *lexer, struct pool *tmp_pool, struct print_trns *trns,
273 struct dictionary *dict,
274 enum which_formats which_formats)
279 if (lex_token (lexer) == T_ENDCMD)
285 while (lex_token (lexer) != T_ENDCMD)
289 if (!parse_record_placement (lexer, &record, &column))
292 if (lex_is_string (lexer))
293 ok = parse_string_argument (lexer, trns, record, &column);
295 ok = parse_variable_argument (lexer, dict, trns, tmp_pool, &record,
296 &column, which_formats);
300 lex_match (lexer, T_COMMA);
303 if (trns->n_records != 0 && trns->n_records != record)
304 msg (SW, _("Output calls for %d records but %zu specified on RECORDS "
306 record, trns->n_records);
307 trns->n_records = record;
312 /* Parses a string argument to the PRINT commands. Returns success. */
314 parse_string_argument (struct lexer *lexer, struct print_trns *trns, int record, int *column)
316 struct prt_out_spec *spec = pool_alloc (trns->pool, sizeof *spec);
317 spec->type = PRT_LITERAL;
318 spec->record = record;
319 spec->first_column = *column;
320 ds_init_substring (&spec->string, lex_tokss (lexer));
321 ds_register_pool (&spec->string, trns->pool);
324 /* Parse the included column range. */
325 if (lex_is_number (lexer))
327 int first_column, last_column;
328 bool range_specified;
330 if (!parse_column_range (lexer, 1,
331 &first_column, &last_column, &range_specified))
334 spec->first_column = first_column;
336 ds_set_length (&spec->string, last_column - first_column + 1, ' ');
339 spec->width = u8_strwidth (CHAR_CAST (const uint8_t *,
340 ds_cstr (&spec->string)),
342 *column = spec->first_column + spec->width;
344 ll_push_tail (&trns->specs, &spec->ll);
348 /* Parses a variable argument to the PRINT commands by passing it off
349 to fixed_parse_compatible() or fixed_parse_fortran() as appropriate.
352 parse_variable_argument (struct lexer *lexer, const struct dictionary *dict,
353 struct print_trns *trns, struct pool *tmp_pool,
354 int *record, int *column,
355 enum which_formats which_formats)
357 const struct variable **vars;
358 size_t n_vars, var_idx;
359 struct fmt_spec *formats, *f;
363 if (!parse_variables_const_pool (lexer, tmp_pool, dict,
364 &vars, &n_vars, PV_DUPLICATE))
367 if (lex_is_number (lexer) || lex_token (lexer) == T_LPAREN)
369 if (!parse_var_placements (lexer, tmp_pool, n_vars, FMT_FOR_OUTPUT,
370 &formats, &n_formats))
378 lex_match (lexer, T_ASTERISK);
380 formats = pool_nmalloc (tmp_pool, n_vars, sizeof *formats);
382 for (i = 0; i < n_vars; i++)
384 const struct variable *v = vars[i];
385 formats[i] = (which_formats == PRINT
386 ? *var_get_print_format (v)
387 : *var_get_write_format (v));
389 add_space = which_formats == PRINT;
393 for (f = formats; f < &formats[n_formats]; f++)
394 if (!execute_placement_format (f, record, column))
396 const struct variable *var;
397 struct prt_out_spec *spec;
399 var = vars[var_idx++];
400 if (!fmt_check_width_compat (f, var_get_width (var)))
403 spec = pool_alloc (trns->pool, sizeof *spec);
404 spec->type = PRT_VAR;
405 spec->record = *record;
406 spec->first_column = *column;
409 spec->add_space = add_space;
411 /* This is a completely bizarre twist for compatibility:
412 WRITE outputs the system-missing value as a field
413 filled with spaces, instead of using the normal format
414 that usually contains a period. */
415 spec->sysmis_as_spaces = (which_formats == WRITE
416 && var_is_numeric (var)
417 && (fmt_get_category (spec->format.type)
420 ll_push_tail (&trns->specs, &spec->ll);
422 *column += f->w + add_space;
424 assert (var_idx == n_vars);
429 /* Prints the table produced by the TABLE subcommand to the listing
432 dump_table (struct print_trns *trns)
434 struct pivot_table *table = pivot_table_create (N_("Print Summary"));
436 pivot_dimension_create (table, PIVOT_AXIS_COLUMN, N_("Attributes"),
437 N_("Record"), N_("Columns"), N_("Format"));
439 struct pivot_dimension *variables = pivot_dimension_create (
440 table, PIVOT_AXIS_ROW, N_("Variable"));
442 struct prt_out_spec *spec;
443 ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
445 if (spec->type != PRT_VAR)
448 int row = pivot_category_create_leaf (
449 variables->root, pivot_value_new_variable (spec->var));
451 pivot_table_put2 (table, 0, row,
452 pivot_value_new_integer (spec->record));
453 int last_column = spec->first_column + spec->format.w - 1;
454 pivot_table_put2 (table, 1, row, pivot_value_new_user_text_nocopy (
456 spec->first_column, last_column)));
458 char fmt_string[FMT_STRING_LEN_MAX + 1];
459 pivot_table_put2 (table, 2, row, pivot_value_new_user_text (
460 fmt_to_string (&spec->format, fmt_string), -1));
463 int row = pivot_category_create_leaf (
464 variables->root, pivot_value_new_text (N_("N of Records")));
465 pivot_table_put2 (table, 0, row,
466 pivot_value_new_integer (trns->n_records));
468 pivot_table_submit (table);
471 /* Transformation, for all-text output. */
473 static void print_text_flush_records (struct print_trns *, struct u8_line *,
475 bool *eject, int *record);
477 /* Performs the transformation inside print_trns T on case C. */
478 static enum trns_result
479 print_text_trns_proc (void *trns_, struct ccase **c,
480 casenumber case_num UNUSED)
482 struct print_trns *trns = trns_;
483 struct prt_out_spec *spec;
486 bool eject = trns->eject;
489 u8_line_init (&line);
490 ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
492 int x0 = spec->first_column;
494 print_text_flush_records (trns, &line, spec->record, &eject, &record);
496 u8_line_set_length (&line, spec->first_column);
497 if (spec->type == PRT_VAR)
499 const union value *input = case_data (*c, spec->var);
502 if (!spec->sysmis_as_spaces || input->f != SYSMIS)
508 s = data_out (input, var_get_encoding (spec->var),
509 &spec->format, settings_get_fmt_settings ());
511 width = u8_width (CHAR_CAST (const uint8_t *, s), len, UTF8);
513 u8_line_put (&line, x0, x1, s, len);
518 int n = spec->format.w;
521 memset (u8_line_reserve (&line, x0, x1, n), ' ', n);
525 *u8_line_reserve (&line, x1, x1 + 1, 1) = ' ';
529 const struct string *s = &spec->string;
531 u8_line_put (&line, x0, x0 + spec->width,
532 ds_data (s), ds_length (s));
535 print_text_flush_records (trns, &line, trns->n_records + 1,
537 u8_line_destroy (&line);
539 if (trns->writer != NULL && dfm_write_error (trns->writer))
541 return TRNS_CONTINUE;
544 /* Advance from *RECORD to TARGET_RECORD, outputting records
545 along the way. If *EJECT is true, then the first record
546 output is preceded by ejecting the page (and *EJECT is set
549 print_text_flush_records (struct print_trns *trns, struct u8_line *line,
550 int target_record, bool *eject, int *record)
552 for (; target_record > *record; (*record)++)
559 if (trns->writer == NULL)
560 output_item_submit (page_break_item_create ());
564 *u8_line_reserve (line, 0, 1, 1) = leader;
566 if (trns->writer == NULL)
567 output_log ("%s", ds_cstr (&line->s) + 1);
570 size_t len = ds_length (&line->s);
571 char *s = ds_cstr (&line->s);
573 if (!trns->include_prefix)
579 dfm_put_record_utf8 (trns->writer, s, len);
584 /* Transformation, for output involving binary. */
586 static void print_binary_flush_records (struct print_trns *,
587 struct string *line, int target_record,
588 bool *eject, int *record);
590 /* Performs the transformation inside print_trns T on case C. */
591 static enum trns_result
592 print_binary_trns_proc (void *trns_, struct ccase **c,
593 casenumber case_num UNUSED)
595 struct print_trns *trns = trns_;
596 bool eject = trns->eject;
597 char encoded_space = recode_byte (trns->encoding, C_ENCODING, ' ');
599 struct prt_out_spec *spec;
602 ds_init_empty (&line);
603 ds_put_byte (&line, ' ');
604 ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
606 print_binary_flush_records (trns, &line, spec->record, &eject, &record);
608 ds_set_length (&line, spec->first_column, encoded_space);
609 if (spec->type == PRT_VAR)
611 const union value *input = case_data (*c, spec->var);
612 if (!spec->sysmis_as_spaces || input->f != SYSMIS)
613 data_out_recode (input, var_get_encoding (spec->var),
614 &spec->format, settings_get_fmt_settings (),
615 &line, trns->encoding);
617 ds_put_byte_multiple (&line, encoded_space, spec->format.w);
619 ds_put_byte (&line, encoded_space);
623 ds_put_substring (&line, ds_ss (&spec->string));
624 if (0 != strcmp (trns->encoding, UTF8))
626 size_t length = ds_length (&spec->string);
627 char *data = ss_data (ds_tail (&line, length));
628 char *s = recode_string (trns->encoding, UTF8, data, length);
629 memcpy (data, s, length);
634 print_binary_flush_records (trns, &line, trns->n_records + 1,
638 if (trns->writer != NULL && dfm_write_error (trns->writer))
640 return TRNS_CONTINUE;
643 /* Advance from *RECORD to TARGET_RECORD, outputting records
644 along the way. If *EJECT is true, then the first record
645 output is preceded by ejecting the page (and *EJECT is set
648 print_binary_flush_records (struct print_trns *trns, struct string *line,
649 int target_record, bool *eject, int *record)
651 for (; target_record > *record; (*record)++)
653 char *s = ds_cstr (line);
654 size_t length = ds_length (line);
662 s[0] = recode_byte (trns->encoding, C_ENCODING, leader);
664 if (!trns->include_prefix)
669 dfm_put_record (trns->writer, s, length);
671 ds_truncate (line, 1);
677 print_trns_free (void *trns_)
679 struct print_trns *trns = trns_;
682 if (trns->writer != NULL)
683 ok = dfm_close_writer (trns->writer);
684 pool_destroy (trns->pool);
689 static const struct trns_class print_binary_trns_class = {
691 .execute = print_binary_trns_proc,
692 .destroy = print_trns_free,
695 static const struct trns_class print_text_trns_class = {
697 .execute = print_text_trns_proc,
698 .destroy = print_trns_free,