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/pivot-table.h"
44 #include "output/tab.h"
45 #include "output/text-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 struct ll ll; /* In struct print_trns `specs' list. */
65 enum field_type type; /* What type of field this is. */
66 int record; /* 1-based record number. */
67 int first_column; /* 0-based first column. */
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 string 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 ll_list specs; /* List of struct prt_out_specs. */
89 size_t record_cnt; /* Number of records to write. */
98 static int internal_cmd_print (struct lexer *, struct dataset *ds,
99 enum which_formats, bool eject);
100 static trns_proc_func print_text_trns_proc, print_binary_trns_proc;
101 static trns_free_func print_trns_free;
102 static bool parse_specs (struct lexer *, struct pool *tmp_pool, struct print_trns *,
103 struct dictionary *dict, enum which_formats);
104 static void dump_table (struct print_trns *);
108 /* Parses PRINT command. */
110 cmd_print (struct lexer *lexer, struct dataset *ds)
112 return internal_cmd_print (lexer, ds, PRINT, false);
115 /* Parses PRINT EJECT command. */
117 cmd_print_eject (struct lexer *lexer, struct dataset *ds)
119 return internal_cmd_print (lexer, ds, PRINT, true);
122 /* Parses WRITE command. */
124 cmd_write (struct lexer *lexer, struct dataset *ds)
126 return internal_cmd_print (lexer, ds, WRITE, false);
129 /* Parses the output commands. */
131 internal_cmd_print (struct lexer *lexer, struct dataset *ds,
132 enum which_formats which_formats, bool eject)
134 bool print_table = false;
135 const struct prt_out_spec *spec;
136 struct print_trns *trns;
137 struct file_handle *fh = NULL;
138 char *encoding = NULL;
139 struct pool *tmp_pool;
142 /* Fill in prt to facilitate error-handling. */
143 trns = pool_create_container (struct print_trns, pool);
146 trns->record_cnt = 0;
147 ll_init (&trns->specs);
149 tmp_pool = pool_create_subpool (trns->pool);
151 /* Parse the command options. */
152 while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
154 if (lex_match_id (lexer, "OUTFILE"))
156 lex_match (lexer, T_EQUALS);
158 fh = fh_parse (lexer, FH_REF_FILE, NULL);
162 else if (lex_match_id (lexer, "ENCODING"))
164 lex_match (lexer, T_EQUALS);
165 if (!lex_force_string (lexer))
169 encoding = ss_xstrdup (lex_tokss (lexer));
173 else if (lex_match_id (lexer, "RECORDS"))
175 lex_match (lexer, T_EQUALS);
176 lex_match (lexer, T_LPAREN);
177 if (!lex_force_int (lexer))
179 trns->record_cnt = lex_integer (lexer);
181 lex_match (lexer, T_RPAREN);
183 else if (lex_match_id (lexer, "TABLE"))
185 else if (lex_match_id (lexer, "NOTABLE"))
189 lex_error (lexer, _("expecting a valid subcommand"));
194 /* When PRINT or PRINT EJECT writes to an external file, we
195 prefix each line with a space for compatibility. */
196 trns->include_prefix = which_formats == PRINT && fh != NULL;
198 /* Parse variables and strings. */
199 if (!parse_specs (lexer, tmp_pool, trns, dataset_dict (ds), which_formats))
202 /* Are there any binary formats?
204 There are real difficulties figuring out what to do when both binary
205 formats and nontrivial encodings enter the picture. So when binary
206 formats are present we fall back to much simpler handling. */
208 ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
210 if (spec->type == PRT_VAR
211 && fmt_get_category (spec->format.type) == FMT_CAT_BINARY)
217 if (binary && fh == NULL)
219 msg (SE, _("%s is required when binary formats are specified."), "OUTFILE");
223 if (lex_end_of_command (lexer) != CMD_SUCCESS)
228 trns->writer = dfm_open_writer (fh, encoding);
229 if (trns->writer == NULL)
231 trns->encoding = dfm_writer_get_encoding (trns->writer);
234 trns->encoding = UTF8;
236 /* Output the variable table if requested. */
240 /* Put the transformation in the queue. */
241 add_transformation (ds,
243 ? print_binary_trns_proc
244 : print_text_trns_proc),
245 print_trns_free, trns);
247 pool_destroy (tmp_pool);
253 print_trns_free (trns);
258 static bool parse_string_argument (struct lexer *, struct print_trns *,
259 int record, int *column);
260 static bool parse_variable_argument (struct lexer *, const struct dictionary *,
262 struct pool *tmp_pool,
263 int *record, int *column,
266 /* Parses all the variable and string specifications on a single
267 PRINT, PRINT EJECT, or WRITE command into the prt structure.
270 parse_specs (struct lexer *lexer, struct pool *tmp_pool, struct print_trns *trns,
271 struct dictionary *dict,
272 enum which_formats which_formats)
277 if (lex_token (lexer) == T_ENDCMD)
279 trns->record_cnt = 1;
283 while (lex_token (lexer) != T_ENDCMD)
287 if (!parse_record_placement (lexer, &record, &column))
290 if (lex_is_string (lexer))
291 ok = parse_string_argument (lexer, trns, record, &column);
293 ok = parse_variable_argument (lexer, dict, trns, tmp_pool, &record,
294 &column, which_formats);
298 lex_match (lexer, T_COMMA);
301 if (trns->record_cnt != 0 && trns->record_cnt != record)
302 msg (SW, _("Output calls for %d records but %zu specified on RECORDS "
304 record, trns->record_cnt);
305 trns->record_cnt = record;
310 /* Parses a string argument to the PRINT commands. Returns success. */
312 parse_string_argument (struct lexer *lexer, struct print_trns *trns, int record, int *column)
314 struct prt_out_spec *spec = pool_alloc (trns->pool, sizeof *spec);
315 spec->type = PRT_LITERAL;
316 spec->record = record;
317 spec->first_column = *column;
318 ds_init_substring (&spec->string, lex_tokss (lexer));
319 ds_register_pool (&spec->string, trns->pool);
322 /* Parse the included column range. */
323 if (lex_is_number (lexer))
325 int first_column, last_column;
326 bool range_specified;
328 if (!parse_column_range (lexer, 1,
329 &first_column, &last_column, &range_specified))
332 spec->first_column = first_column;
334 ds_set_length (&spec->string, last_column - first_column + 1, ' ');
337 spec->width = u8_strwidth (CHAR_CAST (const uint8_t *,
338 ds_cstr (&spec->string)),
340 *column = spec->first_column + spec->width;
342 ll_push_tail (&trns->specs, &spec->ll);
346 /* Parses a variable argument to the PRINT commands by passing it off
347 to fixed_parse_compatible() or fixed_parse_fortran() as appropriate.
350 parse_variable_argument (struct lexer *lexer, const struct dictionary *dict,
351 struct print_trns *trns, struct pool *tmp_pool,
352 int *record, int *column,
353 enum which_formats which_formats)
355 const struct variable **vars;
356 size_t var_cnt, var_idx;
357 struct fmt_spec *formats, *f;
361 if (!parse_variables_const_pool (lexer, tmp_pool, dict,
362 &vars, &var_cnt, PV_DUPLICATE))
365 if (lex_is_number (lexer) || lex_token (lexer) == T_LPAREN)
367 if (!parse_var_placements (lexer, tmp_pool, var_cnt, FMT_FOR_OUTPUT,
368 &formats, &format_cnt))
376 lex_match (lexer, T_ASTERISK);
378 formats = pool_nmalloc (tmp_pool, var_cnt, sizeof *formats);
379 format_cnt = var_cnt;
380 for (i = 0; i < var_cnt; i++)
382 const struct variable *v = vars[i];
383 formats[i] = (which_formats == PRINT
384 ? *var_get_print_format (v)
385 : *var_get_write_format (v));
387 add_space = which_formats == PRINT;
391 for (f = formats; f < &formats[format_cnt]; f++)
392 if (!execute_placement_format (f, record, column))
394 const struct variable *var;
395 struct prt_out_spec *spec;
397 var = vars[var_idx++];
398 if (!fmt_check_width_compat (f, var_get_width (var)))
401 spec = pool_alloc (trns->pool, sizeof *spec);
402 spec->type = PRT_VAR;
403 spec->record = *record;
404 spec->first_column = *column;
407 spec->add_space = add_space;
409 /* This is a completely bizarre twist for compatibility:
410 WRITE outputs the system-missing value as a field
411 filled with spaces, instead of using the normal format
412 that usually contains a period. */
413 spec->sysmis_as_spaces = (which_formats == WRITE
414 && var_is_numeric (var)
415 && (fmt_get_category (spec->format.type)
418 ll_push_tail (&trns->specs, &spec->ll);
420 *column += f->w + add_space;
422 assert (var_idx == var_cnt);
427 /* Prints the table produced by the TABLE subcommand to the listing
430 dump_table (struct print_trns *trns)
432 struct pivot_table *table = pivot_table_create (N_("Print Summary"));
434 pivot_dimension_create (table, PIVOT_AXIS_COLUMN, N_("Attributes"),
435 N_("Record"), N_("Columns"), N_("Format"));
437 struct pivot_dimension *variables = pivot_dimension_create (
438 table, PIVOT_AXIS_ROW, N_("Variable"));
440 struct prt_out_spec *spec;
441 ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
443 if (spec->type != PRT_VAR)
446 int row = pivot_category_create_leaf (
447 variables->root, pivot_value_new_variable (spec->var));
449 pivot_table_put2 (table, 0, row,
450 pivot_value_new_integer (spec->record));
451 int last_column = spec->first_column + spec->format.w - 1;
452 pivot_table_put2 (table, 1, row, pivot_value_new_user_text_nocopy (
453 xasprintf ("%3d-%3d",
454 spec->first_column, last_column)));
456 char fmt_string[FMT_STRING_LEN_MAX + 1];
457 pivot_table_put2 (table, 2, row, pivot_value_new_user_text (
458 fmt_to_string (&spec->format, fmt_string), -1));
461 int row = pivot_category_create_leaf (
462 variables->root, pivot_value_new_text (N_("N of Records")));
463 pivot_table_put2 (table, 0, row,
464 pivot_value_new_integer (trns->record_cnt));
466 pivot_table_submit (table);
469 /* Transformation, for all-text output. */
471 static void print_text_flush_records (struct print_trns *, struct u8_line *,
473 bool *eject, int *record);
475 /* Performs the transformation inside print_trns T on case C. */
477 print_text_trns_proc (void *trns_, struct ccase **c,
478 casenumber case_num UNUSED)
480 struct print_trns *trns = trns_;
481 struct prt_out_spec *spec;
484 bool eject = trns->eject;
487 u8_line_init (&line);
488 ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
490 int x0 = spec->first_column;
492 print_text_flush_records (trns, &line, spec->record, &eject, &record);
494 u8_line_set_length (&line, spec->first_column);
495 if (spec->type == PRT_VAR)
497 const union value *input = case_data (*c, spec->var);
500 if (!spec->sysmis_as_spaces || input->f != SYSMIS)
506 s = data_out (input, var_get_encoding (spec->var),
509 width = u8_width (CHAR_CAST (const uint8_t *, s), len, UTF8);
511 u8_line_put (&line, x0, x1, s, len);
516 int n = spec->format.w;
519 memset (u8_line_reserve (&line, x0, x1, n), ' ', n);
523 *u8_line_reserve (&line, x1, x1 + 1, 1) = ' ';
527 const struct string *s = &spec->string;
529 u8_line_put (&line, x0, x0 + spec->width,
530 ds_data (s), ds_length (s));
533 print_text_flush_records (trns, &line, trns->record_cnt + 1,
535 u8_line_destroy (&line);
537 if (trns->writer != NULL && dfm_write_error (trns->writer))
539 return TRNS_CONTINUE;
542 /* Advance from *RECORD to TARGET_RECORD, outputting records
543 along the way. If *EJECT is true, then the first record
544 output is preceded by ejecting the page (and *EJECT is set
547 print_text_flush_records (struct print_trns *trns, struct u8_line *line,
548 int target_record, bool *eject, int *record)
550 for (; target_record > *record; (*record)++)
557 if (trns->writer == NULL)
558 text_item_submit (text_item_create (TEXT_ITEM_EJECT_PAGE, ""));
562 *u8_line_reserve (line, 0, 1, 1) = leader;
564 if (trns->writer == NULL)
565 tab_output_text (TAB_FIX, ds_cstr (&line->s) + 1);
568 size_t len = ds_length (&line->s);
569 char *s = ds_cstr (&line->s);
571 if (!trns->include_prefix)
577 if (is_encoding_utf8 (trns->encoding))
578 dfm_put_record (trns->writer, s, len);
581 char *recoded = recode_string (trns->encoding, UTF8, s, len);
582 dfm_put_record (trns->writer, recoded, strlen (recoded));
589 /* Transformation, for output involving binary. */
591 static void print_binary_flush_records (struct print_trns *,
592 struct string *line, int target_record,
593 bool *eject, int *record);
595 /* Performs the transformation inside print_trns T on case C. */
597 print_binary_trns_proc (void *trns_, struct ccase **c,
598 casenumber case_num UNUSED)
600 struct print_trns *trns = trns_;
601 bool eject = trns->eject;
602 char encoded_space = recode_byte (trns->encoding, C_ENCODING, ' ');
604 struct prt_out_spec *spec;
607 ds_init_empty (&line);
608 ds_put_byte (&line, ' ');
609 ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
611 print_binary_flush_records (trns, &line, spec->record, &eject, &record);
613 ds_set_length (&line, spec->first_column, encoded_space);
614 if (spec->type == PRT_VAR)
616 const union value *input = case_data (*c, spec->var);
617 if (!spec->sysmis_as_spaces || input->f != SYSMIS)
618 data_out_recode (input, var_get_encoding (spec->var),
619 &spec->format, &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->record_cnt + 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);