7781a317b6a872fd2e15faf4062f7fad2f0ad1fa
[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/tab.h"
44 #include "output/text-item.h"
45
46 #include "gl/xalloc.h"
47
48 #include "gettext.h"
49 #define _(msgid) gettext (msgid)
50
51 /* Describes what to do when an output field is encountered. */
52 enum field_type
53   {
54     PRT_LITERAL,                /* Literal string. */
55     PRT_VAR                     /* Variable. */
56   };
57
58 /* Describes how to output one field. */
59 struct prt_out_spec
60   {
61     /* All fields. */
62     struct ll ll;               /* In struct print_trns `specs' list. */
63     enum field_type type;       /* What type of field this is. */
64     int record;                 /* 1-based record number. */
65     int first_column;           /* 0-based first column. */
66
67     /* PRT_VAR only. */
68     const struct variable *var; /* Associated variable. */
69     struct fmt_spec format;     /* Output spec. */
70     bool add_space;             /* Add trailing space? */
71     bool sysmis_as_spaces;      /* Output SYSMIS as spaces? */
72
73     /* PRT_LITERAL only. */
74     struct string string;       /* String to output. */
75     int width;                  /* Width of 'string', in display columns. */
76   };
77
78 static inline struct prt_out_spec *
79 ll_to_prt_out_spec (struct ll *ll)
80 {
81   return ll_data (ll, struct prt_out_spec, ll);
82 }
83
84 /* PRINT, PRINT EJECT, WRITE private data structure. */
85 struct print_trns
86   {
87     struct pool *pool;          /* Stores related data. */
88     bool eject;                 /* Eject page before printing? */
89     bool include_prefix;        /* Prefix lines with space? */
90     const char *encoding;       /* Encoding to use for output. */
91     struct dfm_writer *writer;  /* Output file, NULL=listing file. */
92     struct ll_list specs;       /* List of struct prt_out_specs. */
93     size_t record_cnt;          /* Number of records to write. */
94   };
95
96 enum which_formats
97   {
98     PRINT,
99     WRITE
100   };
101
102 static int internal_cmd_print (struct lexer *, struct dataset *ds,
103                                enum which_formats, bool eject);
104 static trns_proc_func print_text_trns_proc, print_binary_trns_proc;
105 static trns_free_func print_trns_free;
106 static bool parse_specs (struct lexer *, struct pool *tmp_pool, struct print_trns *,
107                          struct dictionary *dict, enum which_formats);
108 static void dump_table (struct print_trns *, const struct file_handle *);
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->record_cnt = 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 (lexer))
182             goto error;
183           trns->record_cnt = 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, fh);
243
244   /* Put the transformation in the queue. */
245   add_transformation (ds,
246                       (binary
247                        ? print_binary_trns_proc
248                        : print_text_trns_proc),
249                       print_trns_free, trns);
250
251   pool_destroy (tmp_pool);
252   fh_unref (fh);
253
254   return CMD_SUCCESS;
255
256  error:
257   print_trns_free (trns);
258   fh_unref (fh);
259   return CMD_FAILURE;
260 }
261 \f
262 static bool parse_string_argument (struct lexer *, struct print_trns *,
263                                    int record, int *column);
264 static bool parse_variable_argument (struct lexer *, const struct dictionary *,
265                                      struct print_trns *,
266                                      struct pool *tmp_pool,
267                                      int *record, int *column,
268                                      enum which_formats);
269
270 /* Parses all the variable and string specifications on a single
271    PRINT, PRINT EJECT, or WRITE command into the prt structure.
272    Returns success. */
273 static bool
274 parse_specs (struct lexer *lexer, struct pool *tmp_pool, struct print_trns *trns,
275              struct dictionary *dict,
276              enum which_formats which_formats)
277 {
278   int record = 0;
279   int column = 1;
280
281   if (lex_token (lexer) == T_ENDCMD)
282     {
283       trns->record_cnt = 1;
284       return true;
285     }
286
287   while (lex_token (lexer) != T_ENDCMD)
288     {
289       bool ok;
290
291       if (!parse_record_placement (lexer, &record, &column))
292         return false;
293
294       if (lex_is_string (lexer))
295         ok = parse_string_argument (lexer, trns, record, &column);
296       else
297         ok = parse_variable_argument (lexer, dict, trns, tmp_pool, &record,
298                                       &column, which_formats);
299       if (!ok)
300         return 0;
301
302       lex_match (lexer, T_COMMA);
303     }
304
305   if (trns->record_cnt != 0 && trns->record_cnt != record)
306     msg (SW, _("Output calls for %d records but %zu specified on RECORDS "
307                "subcommand."),
308          record, trns->record_cnt);
309   trns->record_cnt = record;
310
311   return true;
312 }
313
314 /* Parses a string argument to the PRINT commands.  Returns success. */
315 static bool
316 parse_string_argument (struct lexer *lexer, struct print_trns *trns, int record, int *column)
317 {
318   struct prt_out_spec *spec = pool_alloc (trns->pool, sizeof *spec);
319   spec->type = PRT_LITERAL;
320   spec->record = record;
321   spec->first_column = *column;
322   ds_init_substring (&spec->string, lex_tokss (lexer));
323   ds_register_pool (&spec->string, trns->pool);
324   lex_get (lexer);
325
326   /* Parse the included column range. */
327   if (lex_is_number (lexer))
328     {
329       int first_column, last_column;
330       bool range_specified;
331
332       if (!parse_column_range (lexer, 1,
333                                &first_column, &last_column, &range_specified))
334         return false;
335
336       spec->first_column = first_column;
337       if (range_specified)
338         ds_set_length (&spec->string, last_column - first_column + 1, ' ');
339     }
340
341   spec->width = u8_strwidth (CHAR_CAST (const uint8_t *,
342                                         ds_cstr (&spec->string)),
343                              UTF8);
344   *column = spec->first_column + spec->width;
345
346   ll_push_tail (&trns->specs, &spec->ll);
347   return true;
348 }
349
350 /* Parses a variable argument to the PRINT commands by passing it off
351    to fixed_parse_compatible() or fixed_parse_fortran() as appropriate.
352    Returns success. */
353 static bool
354 parse_variable_argument (struct lexer *lexer, const struct dictionary *dict,
355                          struct print_trns *trns, struct pool *tmp_pool,
356                          int *record, int *column,
357                          enum which_formats which_formats)
358 {
359   const struct variable **vars;
360   size_t var_cnt, var_idx;
361   struct fmt_spec *formats, *f;
362   size_t format_cnt;
363   bool add_space;
364
365   if (!parse_variables_const_pool (lexer, tmp_pool, dict,
366                              &vars, &var_cnt, PV_DUPLICATE))
367     return false;
368
369   if (lex_is_number (lexer) || lex_token (lexer) == T_LPAREN)
370     {
371       if (!parse_var_placements (lexer, tmp_pool, var_cnt, FMT_FOR_OUTPUT,
372                                  &formats, &format_cnt))
373         return false;
374       add_space = false;
375     }
376   else
377     {
378       size_t i;
379
380       lex_match (lexer, T_ASTERISK);
381
382       formats = pool_nmalloc (tmp_pool, var_cnt, sizeof *formats);
383       format_cnt = var_cnt;
384       for (i = 0; i < var_cnt; i++)
385         {
386           const struct variable *v = vars[i];
387           formats[i] = (which_formats == PRINT
388                         ? *var_get_print_format (v)
389                         : *var_get_write_format (v));
390         }
391       add_space = which_formats == PRINT;
392     }
393
394   var_idx = 0;
395   for (f = formats; f < &formats[format_cnt]; f++)
396     if (!execute_placement_format (f, record, column))
397       {
398         const struct variable *var;
399         struct prt_out_spec *spec;
400
401         var = vars[var_idx++];
402         if (!fmt_check_width_compat (f, 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 == var_cnt);
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, const struct file_handle *fh)
435 {
436   struct prt_out_spec *spec;
437   struct tab_table *t;
438   int spec_cnt;
439   int row;
440
441   spec_cnt = ll_count (&trns->specs);
442   t = tab_create (4, spec_cnt + 1);
443   tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 3, spec_cnt);
444   tab_hline (t, TAL_2, 0, 3, 1);
445   tab_headers (t, 0, 0, 1, 0);
446   tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Variable"));
447   tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Record"));
448   tab_text (t, 2, 0, TAB_CENTER | TAT_TITLE, _("Columns"));
449   tab_text (t, 3, 0, TAB_CENTER | TAT_TITLE, _("Format"));
450   row = 1;
451   ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
452     {
453       char fmt_string[FMT_STRING_LEN_MAX + 1];
454       int width;
455       switch (spec->type)
456         {
457         case PRT_LITERAL:
458           tab_text_format (t, 0, row, TAB_LEFT | TAB_FIX, "`%.*s'",
459                            (int) ds_length (&spec->string),
460                            ds_data (&spec->string));
461           width = ds_length (&spec->string);
462           break;
463         case PRT_VAR:
464           tab_text (t, 0, row, TAB_LEFT, var_get_name (spec->var));
465           tab_text (t, 3, row, TAB_LEFT | TAB_FIX,
466                     fmt_to_string (&spec->format, fmt_string));
467           width = spec->format.w;
468           break;
469         default:
470           NOT_REACHED ();
471         }
472       tab_text_format (t, 1, row, 0, "%d", spec->record);
473       tab_text_format (t, 2, row, 0, "%3d-%3d",
474                        spec->first_column, spec->first_column + width - 1);
475       row++;
476     }
477
478   if (fh != NULL)
479     tab_title (t, ngettext ("Writing %zu record to %s.",
480                             "Writing %zu records to %s.", trns->record_cnt),
481                trns->record_cnt, fh_get_name (fh));
482   else
483     tab_title (t, ngettext ("Writing %zu record.",
484                             "Writing %zu records.", trns->record_cnt),
485                trns->record_cnt);
486   tab_submit (t);
487 }
488 \f
489 /* Transformation, for all-text output. */
490
491 static void print_text_flush_records (struct print_trns *, struct u8_line *,
492                                       int target_record,
493                                       bool *eject, int *record);
494
495 /* Performs the transformation inside print_trns T on case C. */
496 static int
497 print_text_trns_proc (void *trns_, struct ccase **c,
498                       casenumber case_num UNUSED)
499 {
500   struct print_trns *trns = trns_;
501   struct prt_out_spec *spec;
502   struct u8_line line;
503
504   bool eject = trns->eject;
505   int record = 1;
506
507   u8_line_init (&line);
508   ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
509     {
510       int x0 = spec->first_column;
511
512       print_text_flush_records (trns, &line, spec->record, &eject, &record);
513
514       u8_line_set_length (&line, spec->first_column);
515       if (spec->type == PRT_VAR)
516         {
517           const union value *input = case_data (*c, spec->var);
518           int x1;
519
520           if (!spec->sysmis_as_spaces || input->f != SYSMIS)
521             {
522               size_t len;
523               int width;
524               char *s;
525
526               s = data_out (input, var_get_encoding (spec->var),
527                             &spec->format);
528               len = strlen (s);
529               width = u8_width (CHAR_CAST (const uint8_t *, s), len, UTF8);
530               x1 = x0 + width;
531               u8_line_put (&line, x0, x1, s, len);
532               free (s);
533             }
534           else
535             {
536               int n = spec->format.w;
537
538               x1 = x0 + n;
539               memset (u8_line_reserve (&line, x0, x1, n), ' ', n);
540             }
541
542           if (spec->add_space)
543             *u8_line_reserve (&line, x1, x1 + 1, 1) = ' ';
544         }
545       else
546         {
547           const struct string *s = &spec->string;
548
549           u8_line_put (&line, x0, x0 + spec->width,
550                        ds_data (s), ds_length (s));
551         }
552     }
553   print_text_flush_records (trns, &line, trns->record_cnt + 1,
554                             &eject, &record);
555   u8_line_destroy (&line);
556
557   if (trns->writer != NULL && dfm_write_error (trns->writer))
558     return TRNS_ERROR;
559   return TRNS_CONTINUE;
560 }
561
562 /* Advance from *RECORD to TARGET_RECORD, outputting records
563    along the way.  If *EJECT is true, then the first record
564    output is preceded by ejecting the page (and *EJECT is set
565    false). */
566 static void
567 print_text_flush_records (struct print_trns *trns, struct u8_line *line,
568                           int target_record, bool *eject, int *record)
569 {
570   for (; target_record > *record; (*record)++)
571     {
572       char leader = ' ';
573
574       if (*eject)
575         {
576           *eject = false;
577           if (trns->writer == NULL)
578             text_item_submit (text_item_create (TEXT_ITEM_EJECT_PAGE, ""));
579           else
580             leader = '1';
581         }
582       *u8_line_reserve (line, 0, 1, 1) = leader;
583
584       if (trns->writer == NULL)
585         tab_output_text (TAB_FIX, ds_cstr (&line->s) + 1);
586       else
587         {
588           size_t len = ds_length (&line->s);
589           char *s = ds_cstr (&line->s);
590
591           if (!trns->include_prefix)
592             {
593               s++;
594               len--;
595             }
596
597           if (is_encoding_utf8 (trns->encoding))
598             dfm_put_record (trns->writer, s, len);
599           else
600             {
601               char *recoded = recode_string (trns->encoding, UTF8, s, len);
602               dfm_put_record (trns->writer, recoded, strlen (recoded));
603               free (recoded);
604             }
605         }
606     }
607 }
608 \f
609 /* Transformation, for output involving binary. */
610
611 static void print_binary_flush_records (struct print_trns *,
612                                         struct string *line, int target_record,
613                                         bool *eject, int *record);
614
615 /* Performs the transformation inside print_trns T on case C. */
616 static int
617 print_binary_trns_proc (void *trns_, struct ccase **c,
618                         casenumber case_num UNUSED)
619 {
620   struct print_trns *trns = trns_;
621   bool eject = trns->eject;
622   char encoded_space = recode_byte (trns->encoding, C_ENCODING, ' ');
623   int record = 1;
624   struct prt_out_spec *spec;
625   struct string line;
626
627   ds_init_empty (&line);
628   ds_put_byte (&line, ' ');
629   ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
630     {
631       print_binary_flush_records (trns, &line, spec->record, &eject, &record);
632
633       ds_set_length (&line, spec->first_column, encoded_space);
634       if (spec->type == PRT_VAR)
635         {
636           const union value *input = case_data (*c, spec->var);
637           if (!spec->sysmis_as_spaces || input->f != SYSMIS)
638             data_out_recode (input, var_get_encoding (spec->var),
639                              &spec->format, &line, trns->encoding);
640           else
641             ds_put_byte_multiple (&line, encoded_space, spec->format.w);
642           if (spec->add_space)
643             ds_put_byte (&line, encoded_space);
644         }
645       else
646         {
647           ds_put_substring (&line, ds_ss (&spec->string));
648           if (0 != strcmp (trns->encoding, UTF8))
649             {
650               size_t length = ds_length (&spec->string);
651               char *data = ss_data (ds_tail (&line, length));
652               char *s = recode_string (trns->encoding, UTF8, data, length);
653               memcpy (data, s, length);
654               free (s);
655             }
656         }
657     }
658   print_binary_flush_records (trns, &line, trns->record_cnt + 1,
659                               &eject, &record);
660   ds_destroy (&line);
661
662   if (trns->writer != NULL && dfm_write_error (trns->writer))
663     return TRNS_ERROR;
664   return TRNS_CONTINUE;
665 }
666
667 /* Advance from *RECORD to TARGET_RECORD, outputting records
668    along the way.  If *EJECT is true, then the first record
669    output is preceded by ejecting the page (and *EJECT is set
670    false). */
671 static void
672 print_binary_flush_records (struct print_trns *trns, struct string *line,
673                             int target_record, bool *eject, int *record)
674 {
675   for (; target_record > *record; (*record)++)
676     {
677       char *s = ds_cstr (line);
678       size_t length = ds_length (line);
679       char leader = ' ';
680
681       if (*eject)
682         {
683           *eject = false;
684           leader = '1';
685         }
686       s[0] = recode_byte (trns->encoding, C_ENCODING, leader);
687
688       if (!trns->include_prefix)
689         {
690           s++;
691           length--;
692         }
693       dfm_put_record (trns->writer, s, length);
694
695       ds_truncate (line, 1);
696     }
697 }
698 \f
699 /* Frees TRNS. */
700 static bool
701 print_trns_free (void *trns_)
702 {
703   struct print_trns *trns = trns_;
704   bool ok = true;
705
706   if (trns->writer != NULL)
707     ok = dfm_close_writer (trns->writer);
708   pool_destroy (trns->pool);
709
710   return ok;
711 }
712