5c9ef7af993e903c0b0ebf18781e7379b525611f
[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 int internal_cmd_print (struct lexer *, struct dataset *ds,
100                                enum which_formats, bool eject);
101 static trns_proc_func print_text_trns_proc, print_binary_trns_proc;
102 static trns_free_func print_trns_free;
103 static bool parse_specs (struct lexer *, struct pool *tmp_pool, struct print_trns *,
104                          struct dictionary *dict, enum which_formats);
105 static void dump_table (struct print_trns *);
106 \f
107 /* Basic parsing. */
108
109 /* Parses PRINT command. */
110 int
111 cmd_print (struct lexer *lexer, struct dataset *ds)
112 {
113   return internal_cmd_print (lexer, ds, PRINT, false);
114 }
115
116 /* Parses PRINT EJECT command. */
117 int
118 cmd_print_eject (struct lexer *lexer, struct dataset *ds)
119 {
120   return internal_cmd_print (lexer, ds, PRINT, true);
121 }
122
123 /* Parses WRITE command. */
124 int
125 cmd_write (struct lexer *lexer, struct dataset *ds)
126 {
127   return internal_cmd_print (lexer, ds, WRITE, false);
128 }
129
130 /* Parses the output commands. */
131 static int
132 internal_cmd_print (struct lexer *lexer, struct dataset *ds,
133                     enum which_formats which_formats, bool eject)
134 {
135   bool print_table = false;
136   const struct prt_out_spec *spec;
137   struct print_trns *trns;
138   struct file_handle *fh = NULL;
139   char *encoding = NULL;
140   struct pool *tmp_pool;
141   bool binary;
142
143   /* Fill in prt to facilitate error-handling. */
144   trns = pool_create_container (struct print_trns, pool);
145   trns->eject = eject;
146   trns->writer = NULL;
147   trns->n_records = 0;
148   ll_init (&trns->specs);
149
150   tmp_pool = pool_create_subpool (trns->pool);
151
152   /* Parse the command options. */
153   while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
154     {
155       if (lex_match_id (lexer, "OUTFILE"))
156         {
157           lex_match (lexer, T_EQUALS);
158
159           fh = fh_parse (lexer, FH_REF_FILE, NULL);
160           if (fh == NULL)
161             goto error;
162         }
163       else if (lex_match_id (lexer, "ENCODING"))
164         {
165           lex_match (lexer, T_EQUALS);
166           if (!lex_force_string (lexer))
167             goto error;
168
169           free (encoding);
170           encoding = ss_xstrdup (lex_tokss (lexer));
171
172           lex_get (lexer);
173         }
174       else if (lex_match_id (lexer, "RECORDS"))
175         {
176           lex_match (lexer, T_EQUALS);
177           lex_match (lexer, T_LPAREN);
178           if (!lex_force_int_range (lexer, "RECORDS", 0, INT_MAX))
179             goto error;
180           trns->n_records = lex_integer (lexer);
181           lex_get (lexer);
182           lex_match (lexer, T_RPAREN);
183         }
184       else if (lex_match_id (lexer, "TABLE"))
185         print_table = true;
186       else if (lex_match_id (lexer, "NOTABLE"))
187         print_table = false;
188       else
189         {
190           lex_error (lexer, _("expecting a valid subcommand"));
191           goto error;
192         }
193     }
194
195   /* When PRINT or PRINT EJECT writes to an external file, we
196      prefix each line with a space for compatibility. */
197   trns->include_prefix = which_formats == PRINT && fh != NULL;
198
199   /* Parse variables and strings. */
200   if (!parse_specs (lexer, tmp_pool, trns, dataset_dict (ds), which_formats))
201     goto error;
202
203   /* Are there any binary formats?
204
205      There are real difficulties figuring out what to do when both binary
206      formats and nontrivial encodings enter the picture.  So when binary
207      formats are present we fall back to much simpler handling. */
208   binary = false;
209   ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
210     {
211       if (spec->type == PRT_VAR
212           && fmt_get_category (spec->format.type) == FMT_CAT_BINARY)
213         {
214           binary = true;
215           break;
216         }
217     }
218   if (binary && fh == NULL)
219     {
220       msg (SE, _("%s is required when binary formats are specified."), "OUTFILE");
221       goto error;
222     }
223
224   if (lex_end_of_command (lexer) != CMD_SUCCESS)
225     goto error;
226
227   if (fh != NULL)
228     {
229       trns->writer = dfm_open_writer (fh, encoding);
230       if (trns->writer == NULL)
231         goto error;
232       trns->encoding = dfm_writer_get_encoding (trns->writer);
233     }
234   else
235     trns->encoding = UTF8;
236
237   /* Output the variable table if requested. */
238   if (print_table)
239     dump_table (trns);
240
241   /* Put the transformation in the queue. */
242   add_transformation (ds,
243                       (binary
244                        ? print_binary_trns_proc
245                        : print_text_trns_proc),
246                       print_trns_free, trns);
247
248   pool_destroy (tmp_pool);
249   fh_unref (fh);
250
251   return CMD_SUCCESS;
252
253  error:
254   print_trns_free (trns);
255   fh_unref (fh);
256   return CMD_FAILURE;
257 }
258 \f
259 static bool parse_string_argument (struct lexer *, struct print_trns *,
260                                    int record, int *column);
261 static bool parse_variable_argument (struct lexer *, const struct dictionary *,
262                                      struct print_trns *,
263                                      struct pool *tmp_pool,
264                                      int *record, int *column,
265                                      enum which_formats);
266
267 /* Parses all the variable and string specifications on a single
268    PRINT, PRINT EJECT, or WRITE command into the prt structure.
269    Returns success. */
270 static bool
271 parse_specs (struct lexer *lexer, struct pool *tmp_pool, struct print_trns *trns,
272              struct dictionary *dict,
273              enum which_formats which_formats)
274 {
275   int record = 0;
276   int column = 1;
277
278   if (lex_token (lexer) == T_ENDCMD)
279     {
280       trns->n_records = 1;
281       return true;
282     }
283
284   while (lex_token (lexer) != T_ENDCMD)
285     {
286       bool ok;
287
288       if (!parse_record_placement (lexer, &record, &column))
289         return false;
290
291       if (lex_is_string (lexer))
292         ok = parse_string_argument (lexer, trns, record, &column);
293       else
294         ok = parse_variable_argument (lexer, dict, trns, tmp_pool, &record,
295                                       &column, which_formats);
296       if (!ok)
297         return 0;
298
299       lex_match (lexer, T_COMMA);
300     }
301
302   if (trns->n_records != 0 && trns->n_records != record)
303     msg (SW, _("Output calls for %d records but %zu specified on RECORDS "
304                "subcommand."),
305          record, trns->n_records);
306   trns->n_records = record;
307
308   return true;
309 }
310
311 /* Parses a string argument to the PRINT commands.  Returns success. */
312 static bool
313 parse_string_argument (struct lexer *lexer, struct print_trns *trns, int record, int *column)
314 {
315   struct prt_out_spec *spec = pool_alloc (trns->pool, sizeof *spec);
316   spec->type = PRT_LITERAL;
317   spec->record = record;
318   spec->first_column = *column;
319   ds_init_substring (&spec->string, lex_tokss (lexer));
320   ds_register_pool (&spec->string, trns->pool);
321   lex_get (lexer);
322
323   /* Parse the included column range. */
324   if (lex_is_number (lexer))
325     {
326       int first_column, last_column;
327       bool range_specified;
328
329       if (!parse_column_range (lexer, 1,
330                                &first_column, &last_column, &range_specified))
331         return false;
332
333       spec->first_column = first_column;
334       if (range_specified)
335         ds_set_length (&spec->string, last_column - first_column + 1, ' ');
336     }
337
338   spec->width = u8_strwidth (CHAR_CAST (const uint8_t *,
339                                         ds_cstr (&spec->string)),
340                              UTF8);
341   *column = spec->first_column + spec->width;
342
343   ll_push_tail (&trns->specs, &spec->ll);
344   return true;
345 }
346
347 /* Parses a variable argument to the PRINT commands by passing it off
348    to fixed_parse_compatible() or fixed_parse_fortran() as appropriate.
349    Returns success. */
350 static bool
351 parse_variable_argument (struct lexer *lexer, const struct dictionary *dict,
352                          struct print_trns *trns, struct pool *tmp_pool,
353                          int *record, int *column,
354                          enum which_formats which_formats)
355 {
356   const struct variable **vars;
357   size_t n_vars, var_idx;
358   struct fmt_spec *formats, *f;
359   size_t n_formats;
360   bool add_space;
361
362   if (!parse_variables_const_pool (lexer, tmp_pool, dict,
363                              &vars, &n_vars, PV_DUPLICATE))
364     return false;
365
366   if (lex_is_number (lexer) || lex_token (lexer) == T_LPAREN)
367     {
368       if (!parse_var_placements (lexer, tmp_pool, n_vars, FMT_FOR_OUTPUT,
369                                  &formats, &n_formats))
370         return false;
371       add_space = false;
372     }
373   else
374     {
375       size_t i;
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 (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
391   var_idx = 0;
392   for (f = formats; f < &formats[n_formats]; f++)
393     if (!execute_placement_format (f, record, column))
394       {
395         const struct variable *var;
396         struct prt_out_spec *spec;
397
398         var = vars[var_idx++];
399         if (!fmt_check_width_compat (f, var_get_width (var)))
400           return false;
401
402         spec = pool_alloc (trns->pool, sizeof *spec);
403         spec->type = PRT_VAR;
404         spec->record = *record;
405         spec->first_column = *column;
406         spec->var = var;
407         spec->format = *f;
408         spec->add_space = add_space;
409
410         /* This is a completely bizarre twist for compatibility:
411            WRITE outputs the system-missing value as a field
412            filled with spaces, instead of using the normal format
413            that usually contains a period. */
414         spec->sysmis_as_spaces = (which_formats == WRITE
415                                   && var_is_numeric (var)
416                                   && (fmt_get_category (spec->format.type)
417                                       != FMT_CAT_BINARY));
418
419         ll_push_tail (&trns->specs, &spec->ll);
420
421         *column += f->w + add_space;
422       }
423   assert (var_idx == n_vars);
424
425   return true;
426 }
427
428 /* Prints the table produced by the TABLE subcommand to the listing
429    file. */
430 static void
431 dump_table (struct print_trns *trns)
432 {
433   struct pivot_table *table = pivot_table_create (N_("Print Summary"));
434
435   pivot_dimension_create (table, PIVOT_AXIS_COLUMN, N_("Attributes"),
436                           N_("Record"), N_("Columns"), N_("Format"));
437
438   struct pivot_dimension *variables = pivot_dimension_create (
439     table, PIVOT_AXIS_ROW, N_("Variable"));
440
441   struct prt_out_spec *spec;
442   ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
443     {
444       if (spec->type != PRT_VAR)
445         continue;
446
447       int row = pivot_category_create_leaf (
448         variables->root, pivot_value_new_variable (spec->var));
449
450       pivot_table_put2 (table, 0, row,
451                         pivot_value_new_integer (spec->record));
452       int last_column = spec->first_column + spec->format.w - 1;
453       pivot_table_put2 (table, 1, row, pivot_value_new_user_text_nocopy (
454                           xasprintf ("%d-%d",
455                                      spec->first_column, last_column)));
456
457       char fmt_string[FMT_STRING_LEN_MAX + 1];
458       pivot_table_put2 (table, 2, row, pivot_value_new_user_text (
459                           fmt_to_string (&spec->format, fmt_string), -1));
460     }
461
462   int row = pivot_category_create_leaf (
463     variables->root, pivot_value_new_text (N_("N of Records")));
464   pivot_table_put2 (table, 0, row,
465                     pivot_value_new_integer (trns->n_records));
466
467   pivot_table_submit (table);
468 }
469 \f
470 /* Transformation, for all-text output. */
471
472 static void print_text_flush_records (struct print_trns *, struct u8_line *,
473                                       int target_record,
474                                       bool *eject, int *record);
475
476 /* Performs the transformation inside print_trns T on case C. */
477 static int
478 print_text_trns_proc (void *trns_, struct ccase **c,
479                       casenumber case_num UNUSED)
480 {
481   struct print_trns *trns = trns_;
482   struct prt_out_spec *spec;
483   struct u8_line line;
484
485   bool eject = trns->eject;
486   int record = 1;
487
488   u8_line_init (&line);
489   ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
490     {
491       int x0 = spec->first_column;
492
493       print_text_flush_records (trns, &line, spec->record, &eject, &record);
494
495       u8_line_set_length (&line, spec->first_column);
496       if (spec->type == PRT_VAR)
497         {
498           const union value *input = case_data (*c, spec->var);
499           int x1;
500
501           if (!spec->sysmis_as_spaces || input->f != SYSMIS)
502             {
503               size_t len;
504               int width;
505               char *s;
506
507               s = data_out (input, var_get_encoding (spec->var),
508                             &spec->format, settings_get_fmt_settings ());
509               len = strlen (s);
510               width = u8_width (CHAR_CAST (const uint8_t *, s), len, UTF8);
511               x1 = x0 + width;
512               u8_line_put (&line, x0, x1, s, len);
513               free (s);
514             }
515           else
516             {
517               int n = spec->format.w;
518
519               x1 = x0 + n;
520               memset (u8_line_reserve (&line, x0, x1, n), ' ', n);
521             }
522
523           if (spec->add_space)
524             *u8_line_reserve (&line, x1, x1 + 1, 1) = ' ';
525         }
526       else
527         {
528           const struct string *s = &spec->string;
529
530           u8_line_put (&line, x0, x0 + spec->width,
531                        ds_data (s), ds_length (s));
532         }
533     }
534   print_text_flush_records (trns, &line, trns->n_records + 1,
535                             &eject, &record);
536   u8_line_destroy (&line);
537
538   if (trns->writer != NULL && dfm_write_error (trns->writer))
539     return TRNS_ERROR;
540   return TRNS_CONTINUE;
541 }
542
543 /* Advance from *RECORD to TARGET_RECORD, outputting records
544    along the way.  If *EJECT is true, then the first record
545    output is preceded by ejecting the page (and *EJECT is set
546    false). */
547 static void
548 print_text_flush_records (struct print_trns *trns, struct u8_line *line,
549                           int target_record, bool *eject, int *record)
550 {
551   for (; target_record > *record; (*record)++)
552     {
553       char leader = ' ';
554
555       if (*eject)
556         {
557           *eject = false;
558           if (trns->writer == NULL)
559             output_item_submit (page_break_item_create ());
560           else
561             leader = '1';
562         }
563       *u8_line_reserve (line, 0, 1, 1) = leader;
564
565       if (trns->writer == NULL)
566         output_log ("%s", ds_cstr (&line->s) + 1);
567       else
568         {
569           size_t len = ds_length (&line->s);
570           char *s = ds_cstr (&line->s);
571
572           if (!trns->include_prefix)
573             {
574               s++;
575               len--;
576             }
577
578           dfm_put_record_utf8 (trns->writer, s, len);
579         }
580     }
581 }
582 \f
583 /* Transformation, for output involving binary. */
584
585 static void print_binary_flush_records (struct print_trns *,
586                                         struct string *line, int target_record,
587                                         bool *eject, int *record);
588
589 /* Performs the transformation inside print_trns T on case C. */
590 static int
591 print_binary_trns_proc (void *trns_, struct ccase **c,
592                         casenumber case_num UNUSED)
593 {
594   struct print_trns *trns = trns_;
595   bool eject = trns->eject;
596   char encoded_space = recode_byte (trns->encoding, C_ENCODING, ' ');
597   int record = 1;
598   struct prt_out_spec *spec;
599   struct string line;
600
601   ds_init_empty (&line);
602   ds_put_byte (&line, ' ');
603   ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
604     {
605       print_binary_flush_records (trns, &line, spec->record, &eject, &record);
606
607       ds_set_length (&line, spec->first_column, encoded_space);
608       if (spec->type == PRT_VAR)
609         {
610           const union value *input = case_data (*c, spec->var);
611           if (!spec->sysmis_as_spaces || input->f != SYSMIS)
612             data_out_recode (input, var_get_encoding (spec->var),
613                              &spec->format, settings_get_fmt_settings (),
614                              &line, trns->encoding);
615           else
616             ds_put_byte_multiple (&line, encoded_space, spec->format.w);
617           if (spec->add_space)
618             ds_put_byte (&line, encoded_space);
619         }
620       else
621         {
622           ds_put_substring (&line, ds_ss (&spec->string));
623           if (0 != strcmp (trns->encoding, UTF8))
624             {
625               size_t length = ds_length (&spec->string);
626               char *data = ss_data (ds_tail (&line, length));
627               char *s = recode_string (trns->encoding, UTF8, data, length);
628               memcpy (data, s, length);
629               free (s);
630             }
631         }
632     }
633   print_binary_flush_records (trns, &line, trns->n_records + 1,
634                               &eject, &record);
635   ds_destroy (&line);
636
637   if (trns->writer != NULL && dfm_write_error (trns->writer))
638     return TRNS_ERROR;
639   return TRNS_CONTINUE;
640 }
641
642 /* Advance from *RECORD to TARGET_RECORD, outputting records
643    along the way.  If *EJECT is true, then the first record
644    output is preceded by ejecting the page (and *EJECT is set
645    false). */
646 static void
647 print_binary_flush_records (struct print_trns *trns, struct string *line,
648                             int target_record, bool *eject, int *record)
649 {
650   for (; target_record > *record; (*record)++)
651     {
652       char *s = ds_cstr (line);
653       size_t length = ds_length (line);
654       char leader = ' ';
655
656       if (*eject)
657         {
658           *eject = false;
659           leader = '1';
660         }
661       s[0] = recode_byte (trns->encoding, C_ENCODING, leader);
662
663       if (!trns->include_prefix)
664         {
665           s++;
666           length--;
667         }
668       dfm_put_record (trns->writer, s, length);
669
670       ds_truncate (line, 1);
671     }
672 }
673 \f
674 /* Frees TRNS. */
675 static bool
676 print_trns_free (void *trns_)
677 {
678   struct print_trns *trns = trns_;
679   bool ok = true;
680
681   if (trns->writer != NULL)
682     ok = dfm_close_writer (trns->writer);
683   pool_destroy (trns->pool);
684
685   return ok;
686 }
687