output: Introduce pivot tables.
[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/pivot-table.h"
44 #include "output/tab.h"
45 #include "output/text-item.h"
46
47 #include "gl/xalloc.h"
48
49 #include "gettext.h"
50 #define N_(msgid) msgid
51 #define _(msgid) gettext (msgid)
52
53 /* Describes what to do when an output field is encountered. */
54 enum field_type
55   {
56     PRT_LITERAL,                /* Literal string. */
57     PRT_VAR                     /* Variable. */
58   };
59
60 /* Describes how to output one field. */
61 struct prt_out_spec
62   {
63     /* All fields. */
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. */
68
69     /* PRT_VAR only. */
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? */
74
75     /* PRT_LITERAL only. */
76     struct string string;       /* String to output. */
77     int width;                  /* Width of 'string', in display columns. */
78   };
79
80 /* PRINT, PRINT EJECT, WRITE private data structure. */
81 struct print_trns
82   {
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. */
90   };
91
92 enum which_formats
93   {
94     PRINT,
95     WRITE
96   };
97
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 *);
105 \f
106 /* Basic parsing. */
107
108 /* Parses PRINT command. */
109 int
110 cmd_print (struct lexer *lexer, struct dataset *ds)
111 {
112   return internal_cmd_print (lexer, ds, PRINT, false);
113 }
114
115 /* Parses PRINT EJECT command. */
116 int
117 cmd_print_eject (struct lexer *lexer, struct dataset *ds)
118 {
119   return internal_cmd_print (lexer, ds, PRINT, true);
120 }
121
122 /* Parses WRITE command. */
123 int
124 cmd_write (struct lexer *lexer, struct dataset *ds)
125 {
126   return internal_cmd_print (lexer, ds, WRITE, false);
127 }
128
129 /* Parses the output commands. */
130 static int
131 internal_cmd_print (struct lexer *lexer, struct dataset *ds,
132                     enum which_formats which_formats, bool eject)
133 {
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;
140   bool binary;
141
142   /* Fill in prt to facilitate error-handling. */
143   trns = pool_create_container (struct print_trns, pool);
144   trns->eject = eject;
145   trns->writer = NULL;
146   trns->record_cnt = 0;
147   ll_init (&trns->specs);
148
149   tmp_pool = pool_create_subpool (trns->pool);
150
151   /* Parse the command options. */
152   while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
153     {
154       if (lex_match_id (lexer, "OUTFILE"))
155         {
156           lex_match (lexer, T_EQUALS);
157
158           fh = fh_parse (lexer, FH_REF_FILE, NULL);
159           if (fh == NULL)
160             goto error;
161         }
162       else if (lex_match_id (lexer, "ENCODING"))
163         {
164           lex_match (lexer, T_EQUALS);
165           if (!lex_force_string (lexer))
166             goto error;
167
168           free (encoding);
169           encoding = ss_xstrdup (lex_tokss (lexer));
170
171           lex_get (lexer);
172         }
173       else if (lex_match_id (lexer, "RECORDS"))
174         {
175           lex_match (lexer, T_EQUALS);
176           lex_match (lexer, T_LPAREN);
177           if (!lex_force_int (lexer))
178             goto error;
179           trns->record_cnt = lex_integer (lexer);
180           lex_get (lexer);
181           lex_match (lexer, T_RPAREN);
182         }
183       else if (lex_match_id (lexer, "TABLE"))
184         print_table = true;
185       else if (lex_match_id (lexer, "NOTABLE"))
186         print_table = false;
187       else
188         {
189           lex_error (lexer, _("expecting a valid subcommand"));
190           goto error;
191         }
192     }
193
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;
197
198   /* Parse variables and strings. */
199   if (!parse_specs (lexer, tmp_pool, trns, dataset_dict (ds), which_formats))
200     goto error;
201
202   /* Are there any binary formats?
203
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. */
207   binary = false;
208   ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
209     {
210       if (spec->type == PRT_VAR
211           && fmt_get_category (spec->format.type) == FMT_CAT_BINARY)
212         {
213           binary = true;
214           break;
215         }
216     }
217   if (binary && fh == NULL)
218     {
219       msg (SE, _("%s is required when binary formats are specified."), "OUTFILE");
220       goto error;
221     }
222
223   if (lex_end_of_command (lexer) != CMD_SUCCESS)
224     goto error;
225
226   if (fh != NULL)
227     {
228       trns->writer = dfm_open_writer (fh, encoding);
229       if (trns->writer == NULL)
230         goto error;
231       trns->encoding = dfm_writer_get_encoding (trns->writer);
232     }
233   else
234     trns->encoding = UTF8;
235
236   /* Output the variable table if requested. */
237   if (print_table)
238     dump_table (trns);
239
240   /* Put the transformation in the queue. */
241   add_transformation (ds,
242                       (binary
243                        ? print_binary_trns_proc
244                        : print_text_trns_proc),
245                       print_trns_free, trns);
246
247   pool_destroy (tmp_pool);
248   fh_unref (fh);
249
250   return CMD_SUCCESS;
251
252  error:
253   print_trns_free (trns);
254   fh_unref (fh);
255   return CMD_FAILURE;
256 }
257 \f
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 *,
261                                      struct print_trns *,
262                                      struct pool *tmp_pool,
263                                      int *record, int *column,
264                                      enum which_formats);
265
266 /* Parses all the variable and string specifications on a single
267    PRINT, PRINT EJECT, or WRITE command into the prt structure.
268    Returns success. */
269 static bool
270 parse_specs (struct lexer *lexer, struct pool *tmp_pool, struct print_trns *trns,
271              struct dictionary *dict,
272              enum which_formats which_formats)
273 {
274   int record = 0;
275   int column = 1;
276
277   if (lex_token (lexer) == T_ENDCMD)
278     {
279       trns->record_cnt = 1;
280       return true;
281     }
282
283   while (lex_token (lexer) != T_ENDCMD)
284     {
285       bool ok;
286
287       if (!parse_record_placement (lexer, &record, &column))
288         return false;
289
290       if (lex_is_string (lexer))
291         ok = parse_string_argument (lexer, trns, record, &column);
292       else
293         ok = parse_variable_argument (lexer, dict, trns, tmp_pool, &record,
294                                       &column, which_formats);
295       if (!ok)
296         return 0;
297
298       lex_match (lexer, T_COMMA);
299     }
300
301   if (trns->record_cnt != 0 && trns->record_cnt != record)
302     msg (SW, _("Output calls for %d records but %zu specified on RECORDS "
303                "subcommand."),
304          record, trns->record_cnt);
305   trns->record_cnt = record;
306
307   return true;
308 }
309
310 /* Parses a string argument to the PRINT commands.  Returns success. */
311 static bool
312 parse_string_argument (struct lexer *lexer, struct print_trns *trns, int record, int *column)
313 {
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);
320   lex_get (lexer);
321
322   /* Parse the included column range. */
323   if (lex_is_number (lexer))
324     {
325       int first_column, last_column;
326       bool range_specified;
327
328       if (!parse_column_range (lexer, 1,
329                                &first_column, &last_column, &range_specified))
330         return false;
331
332       spec->first_column = first_column;
333       if (range_specified)
334         ds_set_length (&spec->string, last_column - first_column + 1, ' ');
335     }
336
337   spec->width = u8_strwidth (CHAR_CAST (const uint8_t *,
338                                         ds_cstr (&spec->string)),
339                              UTF8);
340   *column = spec->first_column + spec->width;
341
342   ll_push_tail (&trns->specs, &spec->ll);
343   return true;
344 }
345
346 /* Parses a variable argument to the PRINT commands by passing it off
347    to fixed_parse_compatible() or fixed_parse_fortran() as appropriate.
348    Returns success. */
349 static bool
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)
354 {
355   const struct variable **vars;
356   size_t var_cnt, var_idx;
357   struct fmt_spec *formats, *f;
358   size_t format_cnt;
359   bool add_space;
360
361   if (!parse_variables_const_pool (lexer, tmp_pool, dict,
362                              &vars, &var_cnt, PV_DUPLICATE))
363     return false;
364
365   if (lex_is_number (lexer) || lex_token (lexer) == T_LPAREN)
366     {
367       if (!parse_var_placements (lexer, tmp_pool, var_cnt, FMT_FOR_OUTPUT,
368                                  &formats, &format_cnt))
369         return false;
370       add_space = false;
371     }
372   else
373     {
374       size_t i;
375
376       lex_match (lexer, T_ASTERISK);
377
378       formats = pool_nmalloc (tmp_pool, var_cnt, sizeof *formats);
379       format_cnt = var_cnt;
380       for (i = 0; i < var_cnt; i++)
381         {
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));
386         }
387       add_space = which_formats == PRINT;
388     }
389
390   var_idx = 0;
391   for (f = formats; f < &formats[format_cnt]; f++)
392     if (!execute_placement_format (f, record, column))
393       {
394         const struct variable *var;
395         struct prt_out_spec *spec;
396
397         var = vars[var_idx++];
398         if (!fmt_check_width_compat (f, var_get_width (var)))
399           return false;
400
401         spec = pool_alloc (trns->pool, sizeof *spec);
402         spec->type = PRT_VAR;
403         spec->record = *record;
404         spec->first_column = *column;
405         spec->var = var;
406         spec->format = *f;
407         spec->add_space = add_space;
408
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)
416                                       != FMT_CAT_BINARY));
417
418         ll_push_tail (&trns->specs, &spec->ll);
419
420         *column += f->w + add_space;
421       }
422   assert (var_idx == var_cnt);
423
424   return true;
425 }
426
427 /* Prints the table produced by the TABLE subcommand to the listing
428    file. */
429 static void
430 dump_table (struct print_trns *trns)
431 {
432   struct pivot_table *table = pivot_table_create (N_("Print Summary"));
433
434   pivot_dimension_create (table, PIVOT_AXIS_COLUMN, N_("Attributes"),
435                           N_("Record"), N_("Columns"), N_("Format"));
436
437   struct pivot_dimension *variables = pivot_dimension_create (
438     table, PIVOT_AXIS_ROW, N_("Variable"));
439
440   struct prt_out_spec *spec;
441   ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
442     {
443       if (spec->type != PRT_VAR)
444         continue;
445
446       int row = pivot_category_create_leaf (
447         variables->root, pivot_value_new_variable (spec->var));
448
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)));
455
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));
459     }
460
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));
465
466   pivot_table_submit (table);
467 }
468 \f
469 /* Transformation, for all-text output. */
470
471 static void print_text_flush_records (struct print_trns *, struct u8_line *,
472                                       int target_record,
473                                       bool *eject, int *record);
474
475 /* Performs the transformation inside print_trns T on case C. */
476 static int
477 print_text_trns_proc (void *trns_, struct ccase **c,
478                       casenumber case_num UNUSED)
479 {
480   struct print_trns *trns = trns_;
481   struct prt_out_spec *spec;
482   struct u8_line line;
483
484   bool eject = trns->eject;
485   int record = 1;
486
487   u8_line_init (&line);
488   ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
489     {
490       int x0 = spec->first_column;
491
492       print_text_flush_records (trns, &line, spec->record, &eject, &record);
493
494       u8_line_set_length (&line, spec->first_column);
495       if (spec->type == PRT_VAR)
496         {
497           const union value *input = case_data (*c, spec->var);
498           int x1;
499
500           if (!spec->sysmis_as_spaces || input->f != SYSMIS)
501             {
502               size_t len;
503               int width;
504               char *s;
505
506               s = data_out (input, var_get_encoding (spec->var),
507                             &spec->format);
508               len = strlen (s);
509               width = u8_width (CHAR_CAST (const uint8_t *, s), len, UTF8);
510               x1 = x0 + width;
511               u8_line_put (&line, x0, x1, s, len);
512               free (s);
513             }
514           else
515             {
516               int n = spec->format.w;
517
518               x1 = x0 + n;
519               memset (u8_line_reserve (&line, x0, x1, n), ' ', n);
520             }
521
522           if (spec->add_space)
523             *u8_line_reserve (&line, x1, x1 + 1, 1) = ' ';
524         }
525       else
526         {
527           const struct string *s = &spec->string;
528
529           u8_line_put (&line, x0, x0 + spec->width,
530                        ds_data (s), ds_length (s));
531         }
532     }
533   print_text_flush_records (trns, &line, trns->record_cnt + 1,
534                             &eject, &record);
535   u8_line_destroy (&line);
536
537   if (trns->writer != NULL && dfm_write_error (trns->writer))
538     return TRNS_ERROR;
539   return TRNS_CONTINUE;
540 }
541
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
545    false). */
546 static void
547 print_text_flush_records (struct print_trns *trns, struct u8_line *line,
548                           int target_record, bool *eject, int *record)
549 {
550   for (; target_record > *record; (*record)++)
551     {
552       char leader = ' ';
553
554       if (*eject)
555         {
556           *eject = false;
557           if (trns->writer == NULL)
558             text_item_submit (text_item_create (TEXT_ITEM_EJECT_PAGE, ""));
559           else
560             leader = '1';
561         }
562       *u8_line_reserve (line, 0, 1, 1) = leader;
563
564       if (trns->writer == NULL)
565         tab_output_text (TAB_FIX, ds_cstr (&line->s) + 1);
566       else
567         {
568           size_t len = ds_length (&line->s);
569           char *s = ds_cstr (&line->s);
570
571           if (!trns->include_prefix)
572             {
573               s++;
574               len--;
575             }
576
577           if (is_encoding_utf8 (trns->encoding))
578             dfm_put_record (trns->writer, s, len);
579           else
580             {
581               char *recoded = recode_string (trns->encoding, UTF8, s, len);
582               dfm_put_record (trns->writer, recoded, strlen (recoded));
583               free (recoded);
584             }
585         }
586     }
587 }
588 \f
589 /* Transformation, for output involving binary. */
590
591 static void print_binary_flush_records (struct print_trns *,
592                                         struct string *line, int target_record,
593                                         bool *eject, int *record);
594
595 /* Performs the transformation inside print_trns T on case C. */
596 static int
597 print_binary_trns_proc (void *trns_, struct ccase **c,
598                         casenumber case_num UNUSED)
599 {
600   struct print_trns *trns = trns_;
601   bool eject = trns->eject;
602   char encoded_space = recode_byte (trns->encoding, C_ENCODING, ' ');
603   int record = 1;
604   struct prt_out_spec *spec;
605   struct string line;
606
607   ds_init_empty (&line);
608   ds_put_byte (&line, ' ');
609   ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
610     {
611       print_binary_flush_records (trns, &line, spec->record, &eject, &record);
612
613       ds_set_length (&line, spec->first_column, encoded_space);
614       if (spec->type == PRT_VAR)
615         {
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);
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->record_cnt + 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