91d2d9c180c85748b0b71889e6b468a7ac15a244
[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;
360   if (!parse_variables_const_pool (lexer, tmp_pool, dict,
361                                    &vars, &n_vars, PV_DUPLICATE))
362     return false;
363
364   struct fmt_spec *formats, *f;
365   size_t n_formats;
366   bool add_space;
367   int formats_start = lex_ofs (lexer);
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       lex_match (lexer, T_ASTERISK);
378
379       formats = pool_nmalloc (tmp_pool, n_vars, sizeof *formats);
380       n_formats = n_vars;
381       for (size_t i = 0; i < n_vars; i++)
382         {
383           const struct variable *v = vars[i];
384           formats[i] = (which_formats == PRINT
385                         ? *var_get_print_format (v)
386                         : *var_get_write_format (v));
387         }
388       add_space = which_formats == PRINT;
389     }
390   int formats_end = lex_ofs (lexer) - 1;
391
392   size_t var_idx = 0;
393   for (f = formats; f < &formats[n_formats]; f++)
394     if (!execute_placement_format (f, record, column))
395       {
396         const struct variable *var = vars[var_idx++];
397         char *error = fmt_check_width_compat__ (f, var_get_name (var),
398                                                 var_get_width (var));
399         if (error)
400           {
401             lex_ofs_error (lexer, formats_start, formats_end, "%s", error);
402             free (error);
403             return false;
404           }
405
406         struct prt_out_spec *spec = pool_alloc (trns->pool, sizeof *spec);
407         *spec = (struct prt_out_spec) {
408           .type = PRT_VAR,
409           .record = *record,
410           .first_column = *column,
411           .var = var,
412           .format = *f,
413           .add_space = add_space,
414
415           /* This is a completely bizarre twist for compatibility: WRITE
416              outputs the system-missing value as a field filled with spaces,
417              instead of using the normal format that usually contains a
418              period. */
419           .sysmis_as_spaces = (which_formats == WRITE
420                                && var_is_numeric (var)
421                                && (fmt_get_category (f->type)
422                                    != FMT_CAT_BINARY)),
423         };
424         ll_push_tail (&trns->specs, &spec->ll);
425
426         *column += f->w + add_space;
427       }
428   assert (var_idx == n_vars);
429
430   return true;
431 }
432
433 /* Prints the table produced by the TABLE subcommand to the listing
434    file. */
435 static void
436 dump_table (struct print_trns *trns)
437 {
438   struct pivot_table *table = pivot_table_create (N_("Print Summary"));
439
440   pivot_dimension_create (table, PIVOT_AXIS_COLUMN, N_("Attributes"),
441                           N_("Record"), N_("Columns"), N_("Format"));
442
443   struct pivot_dimension *variables = pivot_dimension_create (
444     table, PIVOT_AXIS_ROW, N_("Variable"));
445
446   struct prt_out_spec *spec;
447   ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
448     {
449       if (spec->type != PRT_VAR)
450         continue;
451
452       int row = pivot_category_create_leaf (
453         variables->root, pivot_value_new_variable (spec->var));
454
455       pivot_table_put2 (table, 0, row,
456                         pivot_value_new_integer (spec->record));
457       int last_column = spec->first_column + spec->format.w - 1;
458       pivot_table_put2 (table, 1, row, pivot_value_new_user_text_nocopy (
459                           xasprintf ("%d-%d",
460                                      spec->first_column, last_column)));
461
462       char fmt_string[FMT_STRING_LEN_MAX + 1];
463       pivot_table_put2 (table, 2, row, pivot_value_new_user_text (
464                           fmt_to_string (&spec->format, fmt_string), -1));
465     }
466
467   int row = pivot_category_create_leaf (
468     variables->root, pivot_value_new_text (N_("N of Records")));
469   pivot_table_put2 (table, 0, row,
470                     pivot_value_new_integer (trns->n_records));
471
472   pivot_table_submit (table);
473 }
474 \f
475 /* Transformation, for all-text output. */
476
477 static void print_text_flush_records (struct print_trns *, struct u8_line *,
478                                       int target_record,
479                                       bool *eject, int *record);
480
481 /* Performs the transformation inside print_trns T on case C. */
482 static enum trns_result
483 print_text_trns_proc (void *trns_, struct ccase **c,
484                       casenumber case_num UNUSED)
485 {
486   struct print_trns *trns = trns_;
487   struct prt_out_spec *spec;
488   struct u8_line line;
489
490   bool eject = trns->eject;
491   int record = 1;
492
493   u8_line_init (&line);
494   ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
495     {
496       int x0 = spec->first_column;
497
498       print_text_flush_records (trns, &line, spec->record, &eject, &record);
499
500       u8_line_set_length (&line, spec->first_column);
501       if (spec->type == PRT_VAR)
502         {
503           const union value *input = case_data (*c, spec->var);
504           int x1;
505
506           if (!spec->sysmis_as_spaces || input->f != SYSMIS)
507             {
508               size_t len;
509               int width;
510               char *s;
511
512               s = data_out (input, var_get_encoding (spec->var),
513                             &spec->format, settings_get_fmt_settings ());
514               len = strlen (s);
515               width = u8_width (CHAR_CAST (const uint8_t *, s), len, UTF8);
516               x1 = x0 + width;
517               u8_line_put (&line, x0, x1, s, len);
518               free (s);
519             }
520           else
521             {
522               int n = spec->format.w;
523
524               x1 = x0 + n;
525               memset (u8_line_reserve (&line, x0, x1, n), ' ', n);
526             }
527
528           if (spec->add_space)
529             *u8_line_reserve (&line, x1, x1 + 1, 1) = ' ';
530         }
531       else
532         {
533           const struct string *s = &spec->string;
534
535           u8_line_put (&line, x0, x0 + spec->width,
536                        ds_data (s), ds_length (s));
537         }
538     }
539   print_text_flush_records (trns, &line, trns->n_records + 1,
540                             &eject, &record);
541   u8_line_destroy (&line);
542
543   if (trns->writer != NULL && dfm_write_error (trns->writer))
544     return TRNS_ERROR;
545   return TRNS_CONTINUE;
546 }
547
548 /* Advance from *RECORD to TARGET_RECORD, outputting records
549    along the way.  If *EJECT is true, then the first record
550    output is preceded by ejecting the page (and *EJECT is set
551    false). */
552 static void
553 print_text_flush_records (struct print_trns *trns, struct u8_line *line,
554                           int target_record, bool *eject, int *record)
555 {
556   for (; target_record > *record; (*record)++)
557     {
558       char leader = ' ';
559
560       if (*eject)
561         {
562           *eject = false;
563           if (trns->writer == NULL)
564             output_item_submit (page_break_item_create ());
565           else
566             leader = '1';
567         }
568       *u8_line_reserve (line, 0, 1, 1) = leader;
569
570       if (trns->writer == NULL)
571         output_log ("%s", ds_cstr (&line->s) + 1);
572       else
573         {
574           size_t len = ds_length (&line->s);
575           char *s = ds_cstr (&line->s);
576
577           if (!trns->include_prefix)
578             {
579               s++;
580               len--;
581             }
582
583           dfm_put_record_utf8 (trns->writer, s, len);
584         }
585     }
586 }
587 \f
588 /* Transformation, for output involving binary. */
589
590 static void print_binary_flush_records (struct print_trns *,
591                                         struct string *line, int target_record,
592                                         bool *eject, int *record);
593
594 /* Performs the transformation inside print_trns T on case C. */
595 static enum trns_result
596 print_binary_trns_proc (void *trns_, struct ccase **c,
597                         casenumber case_num UNUSED)
598 {
599   struct print_trns *trns = trns_;
600   bool eject = trns->eject;
601   char encoded_space = recode_byte (trns->encoding, C_ENCODING, ' ');
602   int record = 1;
603   struct prt_out_spec *spec;
604   struct string line;
605
606   ds_init_empty (&line);
607   ds_put_byte (&line, ' ');
608   ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
609     {
610       print_binary_flush_records (trns, &line, spec->record, &eject, &record);
611
612       ds_set_length (&line, spec->first_column, encoded_space);
613       if (spec->type == PRT_VAR)
614         {
615           const union value *input = case_data (*c, spec->var);
616           if (!spec->sysmis_as_spaces || input->f != SYSMIS)
617             data_out_recode (input, var_get_encoding (spec->var),
618                              &spec->format, settings_get_fmt_settings (),
619                              &line, trns->encoding);
620           else
621             ds_put_byte_multiple (&line, encoded_space, spec->format.w);
622           if (spec->add_space)
623             ds_put_byte (&line, encoded_space);
624         }
625       else
626         {
627           ds_put_substring (&line, ds_ss (&spec->string));
628           if (0 != strcmp (trns->encoding, UTF8))
629             {
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);
634               free (s);
635             }
636         }
637     }
638   print_binary_flush_records (trns, &line, trns->n_records + 1,
639                               &eject, &record);
640   ds_destroy (&line);
641
642   if (trns->writer != NULL && dfm_write_error (trns->writer))
643     return TRNS_ERROR;
644   return TRNS_CONTINUE;
645 }
646
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
650    false). */
651 static void
652 print_binary_flush_records (struct print_trns *trns, struct string *line,
653                             int target_record, bool *eject, int *record)
654 {
655   for (; target_record > *record; (*record)++)
656     {
657       char *s = ds_cstr (line);
658       size_t length = ds_length (line);
659       char leader = ' ';
660
661       if (*eject)
662         {
663           *eject = false;
664           leader = '1';
665         }
666       s[0] = recode_byte (trns->encoding, C_ENCODING, leader);
667
668       if (!trns->include_prefix)
669         {
670           s++;
671           length--;
672         }
673       dfm_put_record (trns->writer, s, length);
674
675       ds_truncate (line, 1);
676     }
677 }
678 \f
679 /* Frees TRNS. */
680 static bool
681 print_trns_free (void *trns_)
682 {
683   struct print_trns *trns = trns_;
684   bool ok = true;
685
686   if (trns->writer != NULL)
687     ok = dfm_close_writer (trns->writer);
688   pool_destroy (trns->pool);
689
690   return ok;
691 }
692
693 static const struct trns_class print_binary_trns_class = {
694   .name = "PRINT",
695   .execute = print_binary_trns_proc,
696   .destroy = print_trns_free,
697 };
698
699 static const struct trns_class print_text_trns_class = {
700   .name = "PRINT",
701   .execute = print_text_trns_proc,
702   .destroy = print_trns_free,
703 };
704