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