906730dce389b14bc9fcbdb2a1696055686dd0f1
[pspp] / src / language / data-io / print.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include <stdlib.h>
20 #include <uniwidth.h>
21
22 #include "data/case.h"
23 #include "data/dataset.h"
24 #include "data/data-out.h"
25 #include "data/format.h"
26 #include "data/transformations.h"
27 #include "data/variable.h"
28 #include "language/command.h"
29 #include "language/data-io/data-writer.h"
30 #include "language/data-io/file-handle.h"
31 #include "language/data-io/placement-parser.h"
32 #include "language/lexer/format-parser.h"
33 #include "language/lexer/lexer.h"
34 #include "language/lexer/variable-parser.h"
35 #include "libpspp/assertion.h"
36 #include "libpspp/compiler.h"
37 #include "libpspp/i18n.h"
38 #include "libpspp/ll.h"
39 #include "libpspp/message.h"
40 #include "libpspp/misc.h"
41 #include "libpspp/pool.h"
42 #include "libpspp/u8-line.h"
43 #include "output/driver.h"
44 #include "output/pivot-table.h"
45 #include "output/table.h"
46 #include "output/output-item.h"
47
48 #include "gl/xalloc.h"
49
50 #include "gettext.h"
51 #define N_(msgid) msgid
52 #define _(msgid) gettext (msgid)
53
54 /* Describes what to do when an output field is encountered. */
55 enum field_type
56   {
57     PRT_LITERAL,                /* Literal string. */
58     PRT_VAR                     /* Variable. */
59   };
60
61 /* Describes how to output one field. */
62 struct prt_out_spec
63   {
64     /* All fields. */
65     struct ll ll;               /* In struct print_trns `specs' list. */
66     enum field_type type;       /* What type of field this is. */
67     int record;                 /* 1-based record number. */
68     int first_column;           /* 0-based first column. */
69
70     /* PRT_VAR only. */
71     const struct variable *var; /* Associated variable. */
72     struct fmt_spec format;     /* Output spec. */
73     bool add_space;             /* Add trailing space? */
74     bool sysmis_as_spaces;      /* Output SYSMIS as spaces? */
75
76     /* PRT_LITERAL only. */
77     struct string string;       /* String to output. */
78     int width;                  /* Width of 'string', in display columns. */
79   };
80
81 /* PRINT, PRINT EJECT, WRITE private data structure. */
82 struct print_trns
83   {
84     struct pool *pool;          /* Stores related data. */
85     bool eject;                 /* Eject page before printing? */
86     bool include_prefix;        /* Prefix lines with space? */
87     const char *encoding;       /* Encoding to use for output. */
88     struct dfm_writer *writer;  /* Output file, NULL=listing file. */
89     struct ll_list specs;       /* List of struct prt_out_specs. */
90     size_t n_records;           /* Number of records to write. */
91   };
92
93 enum which_formats
94   {
95     PRINT,
96     WRITE
97   };
98
99 static const struct trns_class print_binary_trns_class;
100 static const struct trns_class print_text_trns_class;
101
102 static int internal_cmd_print (struct lexer *, struct dataset *ds,
103                                enum which_formats, bool eject);
104 static bool parse_specs (struct lexer *, struct pool *tmp_pool, struct print_trns *,
105                          struct dictionary *dict, enum which_formats);
106 static void dump_table (struct print_trns *);
107
108 static bool print_trns_free (void *trns_);
109 \f
110 /* Basic parsing. */
111
112 /* Parses PRINT command. */
113 int
114 cmd_print (struct lexer *lexer, struct dataset *ds)
115 {
116   return internal_cmd_print (lexer, ds, PRINT, false);
117 }
118
119 /* Parses PRINT EJECT command. */
120 int
121 cmd_print_eject (struct lexer *lexer, struct dataset *ds)
122 {
123   return internal_cmd_print (lexer, ds, PRINT, true);
124 }
125
126 /* Parses WRITE command. */
127 int
128 cmd_write (struct lexer *lexer, struct dataset *ds)
129 {
130   return internal_cmd_print (lexer, ds, WRITE, false);
131 }
132
133 /* Parses the output commands. */
134 static int
135 internal_cmd_print (struct lexer *lexer, struct dataset *ds,
136                     enum which_formats which_formats, bool eject)
137 {
138   bool print_table = false;
139   const struct prt_out_spec *spec;
140   struct print_trns *trns;
141   struct file_handle *fh = NULL;
142   char *encoding = NULL;
143   struct pool *tmp_pool;
144   bool binary;
145
146   /* Fill in prt to facilitate error-handling. */
147   trns = pool_create_container (struct print_trns, pool);
148   trns->eject = eject;
149   trns->writer = NULL;
150   trns->n_records = 0;
151   ll_init (&trns->specs);
152
153   tmp_pool = pool_create_subpool (trns->pool);
154
155   /* Parse the command options. */
156   while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
157     {
158       if (lex_match_id (lexer, "OUTFILE"))
159         {
160           lex_match (lexer, T_EQUALS);
161
162           fh = fh_parse (lexer, FH_REF_FILE, NULL);
163           if (fh == NULL)
164             goto error;
165         }
166       else if (lex_match_id (lexer, "ENCODING"))
167         {
168           lex_match (lexer, T_EQUALS);
169           if (!lex_force_string (lexer))
170             goto error;
171
172           free (encoding);
173           encoding = ss_xstrdup (lex_tokss (lexer));
174
175           lex_get (lexer);
176         }
177       else if (lex_match_id (lexer, "RECORDS"))
178         {
179           lex_match (lexer, T_EQUALS);
180           lex_match (lexer, T_LPAREN);
181           if (!lex_force_int_range (lexer, "RECORDS", 0, INT_MAX))
182             goto error;
183           trns->n_records = lex_integer (lexer);
184           lex_get (lexer);
185           lex_match (lexer, T_RPAREN);
186         }
187       else if (lex_match_id (lexer, "TABLE"))
188         print_table = true;
189       else if (lex_match_id (lexer, "NOTABLE"))
190         print_table = false;
191       else
192         {
193           lex_error_expecting (lexer, "OUTFILE", "ENCODING", "RECORDS",
194                                "TABLE", "NOTABLE");
195           goto error;
196         }
197     }
198
199   /* When PRINT or PRINT EJECT writes to an external file, we
200      prefix each line with a space for compatibility. */
201   trns->include_prefix = which_formats == PRINT && fh != NULL;
202
203   /* Parse variables and strings. */
204   if (!parse_specs (lexer, tmp_pool, trns, dataset_dict (ds), which_formats))
205     goto error;
206
207   /* Are there any binary formats?
208
209      There are real difficulties figuring out what to do when both binary
210      formats and nontrivial encodings enter the picture.  So when binary
211      formats are present we fall back to much simpler handling. */
212   binary = false;
213   ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
214     {
215       if (spec->type == PRT_VAR
216           && fmt_get_category (spec->format.type) == FMT_CAT_BINARY)
217         {
218           binary = true;
219           break;
220         }
221     }
222   if (binary && fh == NULL)
223     {
224       msg (SE, _("%s is required when binary formats are specified."), "OUTFILE");
225       goto error;
226     }
227
228   if (lex_end_of_command (lexer) != CMD_SUCCESS)
229     goto error;
230
231   if (fh != NULL)
232     {
233       trns->writer = dfm_open_writer (fh, encoding);
234       if (trns->writer == NULL)
235         goto error;
236       trns->encoding = dfm_writer_get_encoding (trns->writer);
237     }
238   else
239     trns->encoding = UTF8;
240
241   /* Output the variable table if requested. */
242   if (print_table)
243     dump_table (trns);
244
245   /* Put the transformation in the queue. */
246   add_transformation (ds, (binary
247                            ? &print_binary_trns_class
248                            : &print_text_trns_class), trns);
249
250   pool_destroy (tmp_pool);
251   fh_unref (fh);
252
253   return CMD_SUCCESS;
254
255  error:
256   print_trns_free (trns);
257   fh_unref (fh);
258   return CMD_FAILURE;
259 }
260 \f
261 static bool parse_string_argument (struct lexer *, struct print_trns *,
262                                    int record, int *column);
263 static bool parse_variable_argument (struct lexer *, const struct dictionary *,
264                                      struct print_trns *,
265                                      struct pool *tmp_pool,
266                                      int *record, int *column,
267                                      enum which_formats);
268
269 /* Parses all the variable and string specifications on a single
270    PRINT, PRINT EJECT, or WRITE command into the prt structure.
271    Returns success. */
272 static bool
273 parse_specs (struct lexer *lexer, struct pool *tmp_pool, struct print_trns *trns,
274              struct dictionary *dict,
275              enum which_formats which_formats)
276 {
277   int record = 0;
278   int column = 1;
279
280   if (lex_token (lexer) == T_ENDCMD)
281     {
282       trns->n_records = 1;
283       return true;
284     }
285
286   while (lex_token (lexer) != T_ENDCMD)
287     {
288       bool ok;
289
290       if (!parse_record_placement (lexer, &record, &column))
291         return false;
292
293       if (lex_is_string (lexer))
294         ok = parse_string_argument (lexer, trns, record, &column);
295       else
296         ok = parse_variable_argument (lexer, dict, trns, tmp_pool, &record,
297                                       &column, which_formats);
298       if (!ok)
299         return 0;
300
301       lex_match (lexer, T_COMMA);
302     }
303
304   if (trns->n_records != 0 && trns->n_records != record)
305     msg (SW, _("Output calls for %d records but %zu specified on RECORDS "
306                "subcommand."),
307          record, trns->n_records);
308   trns->n_records = record;
309
310   return true;
311 }
312
313 /* Parses a string argument to the PRINT commands.  Returns success. */
314 static bool
315 parse_string_argument (struct lexer *lexer, struct print_trns *trns, int record, int *column)
316 {
317   struct prt_out_spec *spec = pool_alloc (trns->pool, sizeof *spec);
318   spec->type = PRT_LITERAL;
319   spec->record = record;
320   spec->first_column = *column;
321   ds_init_substring (&spec->string, lex_tokss (lexer));
322   ds_register_pool (&spec->string, trns->pool);
323   lex_get (lexer);
324
325   /* Parse the included column range. */
326   if (lex_is_number (lexer))
327     {
328       int first_column, last_column;
329       bool range_specified;
330
331       if (!parse_column_range (lexer, 1,
332                                &first_column, &last_column, &range_specified))
333         return false;
334
335       spec->first_column = first_column;
336       if (range_specified)
337         ds_set_length (&spec->string, last_column - first_column + 1, ' ');
338     }
339
340   spec->width = u8_strwidth (CHAR_CAST (const uint8_t *,
341                                         ds_cstr (&spec->string)),
342                              UTF8);
343   *column = spec->first_column + spec->width;
344
345   ll_push_tail (&trns->specs, &spec->ll);
346   return true;
347 }
348
349 /* Parses a variable argument to the PRINT commands by passing it off
350    to fixed_parse_compatible() or fixed_parse_fortran() as appropriate.
351    Returns success. */
352 static bool
353 parse_variable_argument (struct lexer *lexer, const struct dictionary *dict,
354                          struct print_trns *trns, struct pool *tmp_pool,
355                          int *record, int *column,
356                          enum which_formats which_formats)
357 {
358   const struct variable **vars;
359   size_t n_vars, var_idx;
360   struct fmt_spec *formats, *f;
361   size_t n_formats;
362   bool add_space;
363
364   if (!parse_variables_const_pool (lexer, tmp_pool, dict,
365                              &vars, &n_vars, PV_DUPLICATE))
366     return false;
367
368   if (lex_is_number (lexer) || lex_token (lexer) == T_LPAREN)
369     {
370       if (!parse_var_placements (lexer, tmp_pool, n_vars, FMT_FOR_OUTPUT,
371                                  &formats, &n_formats))
372         return false;
373       add_space = false;
374     }
375   else
376     {
377       size_t i;
378
379       lex_match (lexer, T_ASTERISK);
380
381       formats = pool_nmalloc (tmp_pool, n_vars, sizeof *formats);
382       n_formats = n_vars;
383       for (i = 0; i < n_vars; i++)
384         {
385           const struct variable *v = vars[i];
386           formats[i] = (which_formats == PRINT
387                         ? *var_get_print_format (v)
388                         : *var_get_write_format (v));
389         }
390       add_space = which_formats == PRINT;
391     }
392
393   var_idx = 0;
394   for (f = formats; f < &formats[n_formats]; f++)
395     if (!execute_placement_format (f, record, column))
396       {
397         const struct variable *var;
398         struct prt_out_spec *spec;
399
400         var = vars[var_idx++];
401         if (!fmt_check_width_compat (f, var_get_name (var),
402                                      var_get_width (var)))
403           return false;
404
405         spec = pool_alloc (trns->pool, sizeof *spec);
406         spec->type = PRT_VAR;
407         spec->record = *record;
408         spec->first_column = *column;
409         spec->var = var;
410         spec->format = *f;
411         spec->add_space = add_space;
412
413         /* This is a completely bizarre twist for compatibility:
414            WRITE outputs the system-missing value as a field
415            filled with spaces, instead of using the normal format
416            that usually contains a period. */
417         spec->sysmis_as_spaces = (which_formats == WRITE
418                                   && var_is_numeric (var)
419                                   && (fmt_get_category (spec->format.type)
420                                       != FMT_CAT_BINARY));
421
422         ll_push_tail (&trns->specs, &spec->ll);
423
424         *column += f->w + add_space;
425       }
426   assert (var_idx == n_vars);
427
428   return true;
429 }
430
431 /* Prints the table produced by the TABLE subcommand to the listing
432    file. */
433 static void
434 dump_table (struct print_trns *trns)
435 {
436   struct pivot_table *table = pivot_table_create (N_("Print Summary"));
437
438   pivot_dimension_create (table, PIVOT_AXIS_COLUMN, N_("Attributes"),
439                           N_("Record"), N_("Columns"), N_("Format"));
440
441   struct pivot_dimension *variables = pivot_dimension_create (
442     table, PIVOT_AXIS_ROW, N_("Variable"));
443
444   struct prt_out_spec *spec;
445   ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
446     {
447       if (spec->type != PRT_VAR)
448         continue;
449
450       int row = pivot_category_create_leaf (
451         variables->root, pivot_value_new_variable (spec->var));
452
453       pivot_table_put2 (table, 0, row,
454                         pivot_value_new_integer (spec->record));
455       int last_column = spec->first_column + spec->format.w - 1;
456       pivot_table_put2 (table, 1, row, pivot_value_new_user_text_nocopy (
457                           xasprintf ("%d-%d",
458                                      spec->first_column, last_column)));
459
460       char fmt_string[FMT_STRING_LEN_MAX + 1];
461       pivot_table_put2 (table, 2, row, pivot_value_new_user_text (
462                           fmt_to_string (&spec->format, fmt_string), -1));
463     }
464
465   int row = pivot_category_create_leaf (
466     variables->root, pivot_value_new_text (N_("N of Records")));
467   pivot_table_put2 (table, 0, row,
468                     pivot_value_new_integer (trns->n_records));
469
470   pivot_table_submit (table);
471 }
472 \f
473 /* Transformation, for all-text output. */
474
475 static void print_text_flush_records (struct print_trns *, struct u8_line *,
476                                       int target_record,
477                                       bool *eject, int *record);
478
479 /* Performs the transformation inside print_trns T on case C. */
480 static enum trns_result
481 print_text_trns_proc (void *trns_, struct ccase **c,
482                       casenumber case_num UNUSED)
483 {
484   struct print_trns *trns = trns_;
485   struct prt_out_spec *spec;
486   struct u8_line line;
487
488   bool eject = trns->eject;
489   int record = 1;
490
491   u8_line_init (&line);
492   ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
493     {
494       int x0 = spec->first_column;
495
496       print_text_flush_records (trns, &line, spec->record, &eject, &record);
497
498       u8_line_set_length (&line, spec->first_column);
499       if (spec->type == PRT_VAR)
500         {
501           const union value *input = case_data (*c, spec->var);
502           int x1;
503
504           if (!spec->sysmis_as_spaces || input->f != SYSMIS)
505             {
506               size_t len;
507               int width;
508               char *s;
509
510               s = data_out (input, var_get_encoding (spec->var),
511                             &spec->format, settings_get_fmt_settings ());
512               len = strlen (s);
513               width = u8_width (CHAR_CAST (const uint8_t *, s), len, UTF8);
514               x1 = x0 + width;
515               u8_line_put (&line, x0, x1, s, len);
516               free (s);
517             }
518           else
519             {
520               int n = spec->format.w;
521
522               x1 = x0 + n;
523               memset (u8_line_reserve (&line, x0, x1, n), ' ', n);
524             }
525
526           if (spec->add_space)
527             *u8_line_reserve (&line, x1, x1 + 1, 1) = ' ';
528         }
529       else
530         {
531           const struct string *s = &spec->string;
532
533           u8_line_put (&line, x0, x0 + spec->width,
534                        ds_data (s), ds_length (s));
535         }
536     }
537   print_text_flush_records (trns, &line, trns->n_records + 1,
538                             &eject, &record);
539   u8_line_destroy (&line);
540
541   if (trns->writer != NULL && dfm_write_error (trns->writer))
542     return TRNS_ERROR;
543   return TRNS_CONTINUE;
544 }
545
546 /* Advance from *RECORD to TARGET_RECORD, outputting records
547    along the way.  If *EJECT is true, then the first record
548    output is preceded by ejecting the page (and *EJECT is set
549    false). */
550 static void
551 print_text_flush_records (struct print_trns *trns, struct u8_line *line,
552                           int target_record, bool *eject, int *record)
553 {
554   for (; target_record > *record; (*record)++)
555     {
556       char leader = ' ';
557
558       if (*eject)
559         {
560           *eject = false;
561           if (trns->writer == NULL)
562             output_item_submit (page_break_item_create ());
563           else
564             leader = '1';
565         }
566       *u8_line_reserve (line, 0, 1, 1) = leader;
567
568       if (trns->writer == NULL)
569         output_log ("%s", ds_cstr (&line->s) + 1);
570       else
571         {
572           size_t len = ds_length (&line->s);
573           char *s = ds_cstr (&line->s);
574
575           if (!trns->include_prefix)
576             {
577               s++;
578               len--;
579             }
580
581           dfm_put_record_utf8 (trns->writer, s, len);
582         }
583     }
584 }
585 \f
586 /* Transformation, for output involving binary. */
587
588 static void print_binary_flush_records (struct print_trns *,
589                                         struct string *line, int target_record,
590                                         bool *eject, int *record);
591
592 /* Performs the transformation inside print_trns T on case C. */
593 static enum trns_result
594 print_binary_trns_proc (void *trns_, struct ccase **c,
595                         casenumber case_num UNUSED)
596 {
597   struct print_trns *trns = trns_;
598   bool eject = trns->eject;
599   char encoded_space = recode_byte (trns->encoding, C_ENCODING, ' ');
600   int record = 1;
601   struct prt_out_spec *spec;
602   struct string line;
603
604   ds_init_empty (&line);
605   ds_put_byte (&line, ' ');
606   ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
607     {
608       print_binary_flush_records (trns, &line, spec->record, &eject, &record);
609
610       ds_set_length (&line, spec->first_column, encoded_space);
611       if (spec->type == PRT_VAR)
612         {
613           const union value *input = case_data (*c, spec->var);
614           if (!spec->sysmis_as_spaces || input->f != SYSMIS)
615             data_out_recode (input, var_get_encoding (spec->var),
616                              &spec->format, settings_get_fmt_settings (),
617                              &line, trns->encoding);
618           else
619             ds_put_byte_multiple (&line, encoded_space, spec->format.w);
620           if (spec->add_space)
621             ds_put_byte (&line, encoded_space);
622         }
623       else
624         {
625           ds_put_substring (&line, ds_ss (&spec->string));
626           if (0 != strcmp (trns->encoding, UTF8))
627             {
628               size_t length = ds_length (&spec->string);
629               char *data = ss_data (ds_tail (&line, length));
630               char *s = recode_string (trns->encoding, UTF8, data, length);
631               memcpy (data, s, length);
632               free (s);
633             }
634         }
635     }
636   print_binary_flush_records (trns, &line, trns->n_records + 1,
637                               &eject, &record);
638   ds_destroy (&line);
639
640   if (trns->writer != NULL && dfm_write_error (trns->writer))
641     return TRNS_ERROR;
642   return TRNS_CONTINUE;
643 }
644
645 /* Advance from *RECORD to TARGET_RECORD, outputting records
646    along the way.  If *EJECT is true, then the first record
647    output is preceded by ejecting the page (and *EJECT is set
648    false). */
649 static void
650 print_binary_flush_records (struct print_trns *trns, struct string *line,
651                             int target_record, bool *eject, int *record)
652 {
653   for (; target_record > *record; (*record)++)
654     {
655       char *s = ds_cstr (line);
656       size_t length = ds_length (line);
657       char leader = ' ';
658
659       if (*eject)
660         {
661           *eject = false;
662           leader = '1';
663         }
664       s[0] = recode_byte (trns->encoding, C_ENCODING, leader);
665
666       if (!trns->include_prefix)
667         {
668           s++;
669           length--;
670         }
671       dfm_put_record (trns->writer, s, length);
672
673       ds_truncate (line, 1);
674     }
675 }
676 \f
677 /* Frees TRNS. */
678 static bool
679 print_trns_free (void *trns_)
680 {
681   struct print_trns *trns = trns_;
682   bool ok = true;
683
684   if (trns->writer != NULL)
685     ok = dfm_close_writer (trns->writer);
686   pool_destroy (trns->pool);
687
688   return ok;
689 }
690
691 static const struct trns_class print_binary_trns_class = {
692   .name = "PRINT",
693   .execute = print_binary_trns_proc,
694   .destroy = print_trns_free,
695 };
696
697 static const struct trns_class print_text_trns_class = {
698   .name = "PRINT",
699   .execute = print_text_trns_proc,
700   .destroy = print_trns_free,
701 };
702