3b5f901b5342bfb4ab16028adbd8769b2980ca22
[pspp] / src / language / data-io / print.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
17 #include <config.h>
18
19 #include <stdlib.h>
20 #include <uniwidth.h>
21
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"
47
48 #include "gl/xalloc.h"
49
50 #include "gettext.h"
51 #define N_(msgid) msgid
52 #define _(msgid) gettext (msgid)
53
54 /* Describes what to do when an output field is encountered. */
55 enum field_type
56   {
57     PRT_LITERAL,                /* Literal string. */
58     PRT_VAR                     /* Variable. */
59   };
60
61 /* Describes how to output one field. */
62 struct prt_out_spec
63   {
64     /* All fields. */
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. */
69
70     /* PRT_VAR only. */
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? */
75
76     /* PRT_LITERAL only. */
77     struct string string;       /* String to output. */
78     int width;                  /* Width of 'string', in display columns. */
79   };
80
81 /* PRINT, PRINT EJECT, WRITE private data structure. */
82 struct print_trns
83   {
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. */
91   };
92
93 enum which_formats
94   {
95     PRINT,
96     WRITE
97   };
98
99 static const struct trns_class print_binary_trns_class;
100 static const struct trns_class print_text_trns_class;
101
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 *);
107
108 static bool print_trns_free (void *trns_);
109 \f
110 /* Basic parsing. */
111
112 /* Parses PRINT command. */
113 int
114 cmd_print (struct lexer *lexer, struct dataset *ds)
115 {
116   return internal_cmd_print (lexer, ds, PRINT, false);
117 }
118
119 /* Parses PRINT EJECT command. */
120 int
121 cmd_print_eject (struct lexer *lexer, struct dataset *ds)
122 {
123   return internal_cmd_print (lexer, ds, PRINT, true);
124 }
125
126 /* Parses WRITE command. */
127 int
128 cmd_write (struct lexer *lexer, struct dataset *ds)
129 {
130   return internal_cmd_print (lexer, ds, WRITE, false);
131 }
132
133 /* Parses the output commands. */
134 static int
135 internal_cmd_print (struct lexer *lexer, struct dataset *ds,
136                     enum which_formats which_formats, bool eject)
137 {
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;
144   bool binary;
145
146   /* Fill in prt to facilitate error-handling. */
147   trns = pool_create_container (struct print_trns, pool);
148   trns->eject = eject;
149   trns->writer = NULL;
150   trns->n_records = 0;
151   ll_init (&trns->specs);
152
153   tmp_pool = pool_create_subpool (trns->pool);
154
155   /* Parse the command options. */
156   while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
157     {
158       if (lex_match_id (lexer, "OUTFILE"))
159         {
160           lex_match (lexer, T_EQUALS);
161
162           fh = fh_parse (lexer, FH_REF_FILE, NULL);
163           if (fh == NULL)
164             goto error;
165         }
166       else if (lex_match_id (lexer, "ENCODING"))
167         {
168           lex_match (lexer, T_EQUALS);
169           if (!lex_force_string (lexer))
170             goto error;
171
172           free (encoding);
173           encoding = ss_xstrdup (lex_tokss (lexer));
174
175           lex_get (lexer);
176         }
177       else if (lex_match_id (lexer, "RECORDS"))
178         {
179           lex_match (lexer, T_EQUALS);
180           lex_match (lexer, T_LPAREN);
181           if (!lex_force_int_range (lexer, "RECORDS", 0, INT_MAX))
182             goto error;
183           trns->n_records = lex_integer (lexer);
184           lex_get (lexer);
185           lex_match (lexer, T_RPAREN);
186         }
187       else if (lex_match_id (lexer, "TABLE"))
188         print_table = true;
189       else if (lex_match_id (lexer, "NOTABLE"))
190         print_table = false;
191       else
192         {
193           lex_error_expecting (lexer, "OUTFILE", "ENCODING", "RECORDS",
194                                "TABLE", "NOTABLE");
195           goto error;
196         }
197     }
198
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;
202
203   /* Parse variables and strings. */
204   if (!parse_specs (lexer, tmp_pool, trns, dataset_dict (ds), which_formats))
205     goto error;
206
207   /* Are there any binary formats?
208
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. */
212   binary = false;
213   ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
214     {
215       if (spec->type == PRT_VAR
216           && fmt_get_category (spec->format.type) == FMT_CAT_BINARY)
217         {
218           binary = true;
219           break;
220         }
221     }
222   if (binary && fh == NULL)
223     {
224       msg (SE, _("%s is required when binary formats are specified."), "OUTFILE");
225       goto error;
226     }
227
228   if (lex_end_of_command (lexer) != CMD_SUCCESS)
229     goto error;
230
231   if (fh != NULL)
232     {
233       trns->writer = dfm_open_writer (fh, encoding);
234       if (trns->writer == NULL)
235         goto error;
236       trns->encoding = dfm_writer_get_encoding (trns->writer);
237     }
238   else
239     trns->encoding = UTF8;
240
241   /* Output the variable table if requested. */
242   if (print_table)
243     dump_table (trns);
244
245   /* Put the transformation in the queue. */
246   add_transformation (ds, (binary
247                            ? &print_binary_trns_class
248                            : &print_text_trns_class), trns);
249
250   pool_destroy (tmp_pool);
251   fh_unref (fh);
252
253   return CMD_SUCCESS;
254
255  error:
256   print_trns_free (trns);
257   fh_unref (fh);
258   return CMD_FAILURE;
259 }
260 \f
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 *,
264                                      struct print_trns *,
265                                      struct pool *tmp_pool,
266                                      int *record, int *column,
267                                      enum which_formats);
268
269 /* Parses all the variable and string specifications on a single
270    PRINT, PRINT EJECT, or WRITE command into the prt structure.
271    Returns success. */
272 static bool
273 parse_specs (struct lexer *lexer, struct pool *tmp_pool, struct print_trns *trns,
274              struct dictionary *dict,
275              enum which_formats which_formats)
276 {
277   int record = 0;
278   int column = 1;
279
280   if (lex_token (lexer) == T_ENDCMD)
281     {
282       trns->n_records = 1;
283       return true;
284     }
285
286   while (lex_token (lexer) != T_ENDCMD)
287     {
288       bool ok;
289
290       if (!parse_record_placement (lexer, &record, &column))
291         return false;
292
293       if (lex_is_string (lexer))
294         ok = parse_string_argument (lexer, trns, record, &column);
295       else
296         ok = parse_variable_argument (lexer, dict, trns, tmp_pool, &record,
297                                       &column, which_formats);
298       if (!ok)
299         return 0;
300
301       lex_match (lexer, T_COMMA);
302     }
303
304   if (trns->n_records != 0 && trns->n_records != record)
305     msg (SW, _("Output calls for %d records but %zu specified on RECORDS "
306                "subcommand."),
307          record, trns->n_records);
308   trns->n_records = record;
309
310   return true;
311 }
312
313 /* Parses a string argument to the PRINT commands.  Returns success. */
314 static bool
315 parse_string_argument (struct lexer *lexer, struct print_trns *trns, int record, int *column)
316 {
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);
323   lex_get (lexer);
324
325   /* Parse the included column range. */
326   if (lex_is_number (lexer))
327     {
328       int first_column, last_column;
329       bool range_specified;
330
331       if (!parse_column_range (lexer, 1,
332                                &first_column, &last_column, &range_specified))
333         return false;
334
335       spec->first_column = first_column;
336       if (range_specified)
337         ds_set_length (&spec->string, last_column - first_column + 1, ' ');
338     }
339
340   spec->width = u8_strwidth (CHAR_CAST (const uint8_t *,
341                                         ds_cstr (&spec->string)),
342                              UTF8);
343   *column = spec->first_column + spec->width;
344
345   ll_push_tail (&trns->specs, &spec->ll);
346   return true;
347 }
348
349 /* Parses a variable argument to the PRINT commands by passing it off
350    to fixed_parse_compatible() or fixed_parse_fortran() as appropriate.
351    Returns success. */
352 static bool
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)
357 {
358   const struct variable **vars;
359   size_t n_vars, var_idx;
360   struct fmt_spec *formats, *f;
361   size_t n_formats;
362   bool add_space;
363
364   if (!parse_variables_const_pool (lexer, tmp_pool, dict,
365                              &vars, &n_vars, PV_DUPLICATE))
366     return false;
367
368   if (lex_is_number (lexer) || lex_token (lexer) == T_LPAREN)
369     {
370       if (!parse_var_placements (lexer, tmp_pool, n_vars, FMT_FOR_OUTPUT,
371                                  &formats, &n_formats))
372         return false;
373       add_space = false;
374     }
375   else
376     {
377       size_t i;
378
379       lex_match (lexer, T_ASTERISK);
380
381       formats = pool_nmalloc (tmp_pool, n_vars, sizeof *formats);
382       n_formats = n_vars;
383       for (i = 0; i < n_vars; i++)
384         {
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));
389         }
390       add_space = which_formats == PRINT;
391     }
392
393   var_idx = 0;
394   for (f = formats; f < &formats[n_formats]; f++)
395     if (!execute_placement_format (f, record, column))
396       {
397         const struct variable *var;
398         struct prt_out_spec *spec;
399
400         var = vars[var_idx++];
401         if (!fmt_check_width_compat (f, var_get_width (var)))
402           return false;
403
404         spec = pool_alloc (trns->pool, sizeof *spec);
405         spec->type = PRT_VAR;
406         spec->record = *record;
407         spec->first_column = *column;
408         spec->var = var;
409         spec->format = *f;
410         spec->add_space = add_space;
411
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)
419                                       != FMT_CAT_BINARY));
420
421         ll_push_tail (&trns->specs, &spec->ll);
422
423         *column += f->w + add_space;
424       }
425   assert (var_idx == n_vars);
426
427   return true;
428 }
429
430 /* Prints the table produced by the TABLE subcommand to the listing
431    file. */
432 static void
433 dump_table (struct print_trns *trns)
434 {
435   struct pivot_table *table = pivot_table_create (N_("Print Summary"));
436
437   pivot_dimension_create (table, PIVOT_AXIS_COLUMN, N_("Attributes"),
438                           N_("Record"), N_("Columns"), N_("Format"));
439
440   struct pivot_dimension *variables = pivot_dimension_create (
441     table, PIVOT_AXIS_ROW, N_("Variable"));
442
443   struct prt_out_spec *spec;
444   ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
445     {
446       if (spec->type != PRT_VAR)
447         continue;
448
449       int row = pivot_category_create_leaf (
450         variables->root, pivot_value_new_variable (spec->var));
451
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 (
456                           xasprintf ("%d-%d",
457                                      spec->first_column, last_column)));
458
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));
462     }
463
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));
468
469   pivot_table_submit (table);
470 }
471 \f
472 /* Transformation, for all-text output. */
473
474 static void print_text_flush_records (struct print_trns *, struct u8_line *,
475                                       int target_record,
476                                       bool *eject, int *record);
477
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)
482 {
483   struct print_trns *trns = trns_;
484   struct prt_out_spec *spec;
485   struct u8_line line;
486
487   bool eject = trns->eject;
488   int record = 1;
489
490   u8_line_init (&line);
491   ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
492     {
493       int x0 = spec->first_column;
494
495       print_text_flush_records (trns, &line, spec->record, &eject, &record);
496
497       u8_line_set_length (&line, spec->first_column);
498       if (spec->type == PRT_VAR)
499         {
500           const union value *input = case_data (*c, spec->var);
501           int x1;
502
503           if (!spec->sysmis_as_spaces || input->f != SYSMIS)
504             {
505               size_t len;
506               int width;
507               char *s;
508
509               s = data_out (input, var_get_encoding (spec->var),
510                             &spec->format, settings_get_fmt_settings ());
511               len = strlen (s);
512               width = u8_width (CHAR_CAST (const uint8_t *, s), len, UTF8);
513               x1 = x0 + width;
514               u8_line_put (&line, x0, x1, s, len);
515               free (s);
516             }
517           else
518             {
519               int n = spec->format.w;
520
521               x1 = x0 + n;
522               memset (u8_line_reserve (&line, x0, x1, n), ' ', n);
523             }
524
525           if (spec->add_space)
526             *u8_line_reserve (&line, x1, x1 + 1, 1) = ' ';
527         }
528       else
529         {
530           const struct string *s = &spec->string;
531
532           u8_line_put (&line, x0, x0 + spec->width,
533                        ds_data (s), ds_length (s));
534         }
535     }
536   print_text_flush_records (trns, &line, trns->n_records + 1,
537                             &eject, &record);
538   u8_line_destroy (&line);
539
540   if (trns->writer != NULL && dfm_write_error (trns->writer))
541     return TRNS_ERROR;
542   return TRNS_CONTINUE;
543 }
544
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
548    false). */
549 static void
550 print_text_flush_records (struct print_trns *trns, struct u8_line *line,
551                           int target_record, bool *eject, int *record)
552 {
553   for (; target_record > *record; (*record)++)
554     {
555       char leader = ' ';
556
557       if (*eject)
558         {
559           *eject = false;
560           if (trns->writer == NULL)
561             output_item_submit (page_break_item_create ());
562           else
563             leader = '1';
564         }
565       *u8_line_reserve (line, 0, 1, 1) = leader;
566
567       if (trns->writer == NULL)
568         output_log ("%s", ds_cstr (&line->s) + 1);
569       else
570         {
571           size_t len = ds_length (&line->s);
572           char *s = ds_cstr (&line->s);
573
574           if (!trns->include_prefix)
575             {
576               s++;
577               len--;
578             }
579
580           dfm_put_record_utf8 (trns->writer, s, len);
581         }
582     }
583 }
584 \f
585 /* Transformation, for output involving binary. */
586
587 static void print_binary_flush_records (struct print_trns *,
588                                         struct string *line, int target_record,
589                                         bool *eject, int *record);
590
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)
595 {
596   struct print_trns *trns = trns_;
597   bool eject = trns->eject;
598   char encoded_space = recode_byte (trns->encoding, C_ENCODING, ' ');
599   int record = 1;
600   struct prt_out_spec *spec;
601   struct string line;
602
603   ds_init_empty (&line);
604   ds_put_byte (&line, ' ');
605   ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
606     {
607       print_binary_flush_records (trns, &line, spec->record, &eject, &record);
608
609       ds_set_length (&line, spec->first_column, encoded_space);
610       if (spec->type == PRT_VAR)
611         {
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);
617           else
618             ds_put_byte_multiple (&line, encoded_space, spec->format.w);
619           if (spec->add_space)
620             ds_put_byte (&line, encoded_space);
621         }
622       else
623         {
624           ds_put_substring (&line, ds_ss (&spec->string));
625           if (0 != strcmp (trns->encoding, UTF8))
626             {
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);
631               free (s);
632             }
633         }
634     }
635   print_binary_flush_records (trns, &line, trns->n_records + 1,
636                               &eject, &record);
637   ds_destroy (&line);
638
639   if (trns->writer != NULL && dfm_write_error (trns->writer))
640     return TRNS_ERROR;
641   return TRNS_CONTINUE;
642 }
643
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
647    false). */
648 static void
649 print_binary_flush_records (struct print_trns *trns, struct string *line,
650                             int target_record, bool *eject, int *record)
651 {
652   for (; target_record > *record; (*record)++)
653     {
654       char *s = ds_cstr (line);
655       size_t length = ds_length (line);
656       char leader = ' ';
657
658       if (*eject)
659         {
660           *eject = false;
661           leader = '1';
662         }
663       s[0] = recode_byte (trns->encoding, C_ENCODING, leader);
664
665       if (!trns->include_prefix)
666         {
667           s++;
668           length--;
669         }
670       dfm_put_record (trns->writer, s, length);
671
672       ds_truncate (line, 1);
673     }
674 }
675 \f
676 /* Frees TRNS. */
677 static bool
678 print_trns_free (void *trns_)
679 {
680   struct print_trns *trns = trns_;
681   bool ok = true;
682
683   if (trns->writer != NULL)
684     ok = dfm_close_writer (trns->writer);
685   pool_destroy (trns->pool);
686
687   return ok;
688 }
689
690 static const struct trns_class print_binary_trns_class = {
691   .name = "PRINT",
692   .execute = print_binary_trns_proc,
693   .destroy = print_trns_free,
694 };
695
696 static const struct trns_class print_text_trns_class = {
697   .name = "PRINT",
698   .execute = print_text_trns_proc,
699   .destroy = print_trns_free,
700 };
701