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