PRINT: Remove unused function.
[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 /* PRINT, PRINT EJECT, WRITE private data structure. */
79 struct print_trns
80   {
81     struct pool *pool;          /* Stores related data. */
82     bool eject;                 /* Eject page before printing? */
83     bool include_prefix;        /* Prefix lines with space? */
84     const char *encoding;       /* Encoding to use for output. */
85     struct dfm_writer *writer;  /* Output file, NULL=listing file. */
86     struct ll_list specs;       /* List of struct prt_out_specs. */
87     size_t record_cnt;          /* Number of records to write. */
88   };
89
90 enum which_formats
91   {
92     PRINT,
93     WRITE
94   };
95
96 static int internal_cmd_print (struct lexer *, struct dataset *ds,
97                                enum which_formats, bool eject);
98 static trns_proc_func print_text_trns_proc, print_binary_trns_proc;
99 static trns_free_func print_trns_free;
100 static bool parse_specs (struct lexer *, struct pool *tmp_pool, struct print_trns *,
101                          struct dictionary *dict, enum which_formats);
102 static void dump_table (struct print_trns *, const struct file_handle *);
103 \f
104 /* Basic parsing. */
105
106 /* Parses PRINT command. */
107 int
108 cmd_print (struct lexer *lexer, struct dataset *ds)
109 {
110   return internal_cmd_print (lexer, ds, PRINT, false);
111 }
112
113 /* Parses PRINT EJECT command. */
114 int
115 cmd_print_eject (struct lexer *lexer, struct dataset *ds)
116 {
117   return internal_cmd_print (lexer, ds, PRINT, true);
118 }
119
120 /* Parses WRITE command. */
121 int
122 cmd_write (struct lexer *lexer, struct dataset *ds)
123 {
124   return internal_cmd_print (lexer, ds, WRITE, false);
125 }
126
127 /* Parses the output commands. */
128 static int
129 internal_cmd_print (struct lexer *lexer, struct dataset *ds,
130                     enum which_formats which_formats, bool eject)
131 {
132   bool print_table = false;
133   const struct prt_out_spec *spec;
134   struct print_trns *trns;
135   struct file_handle *fh = NULL;
136   char *encoding = NULL;
137   struct pool *tmp_pool;
138   bool binary;
139
140   /* Fill in prt to facilitate error-handling. */
141   trns = pool_create_container (struct print_trns, pool);
142   trns->eject = eject;
143   trns->writer = NULL;
144   trns->record_cnt = 0;
145   ll_init (&trns->specs);
146
147   tmp_pool = pool_create_subpool (trns->pool);
148
149   /* Parse the command options. */
150   while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
151     {
152       if (lex_match_id (lexer, "OUTFILE"))
153         {
154           lex_match (lexer, T_EQUALS);
155
156           fh = fh_parse (lexer, FH_REF_FILE, NULL);
157           if (fh == NULL)
158             goto error;
159         }
160       else if (lex_match_id (lexer, "ENCODING"))
161         {
162           lex_match (lexer, T_EQUALS);
163           if (!lex_force_string (lexer))
164             goto error;
165
166           free (encoding);
167           encoding = ss_xstrdup (lex_tokss (lexer));
168
169           lex_get (lexer);
170         }
171       else if (lex_match_id (lexer, "RECORDS"))
172         {
173           lex_match (lexer, T_EQUALS);
174           lex_match (lexer, T_LPAREN);
175           if (!lex_force_int (lexer))
176             goto error;
177           trns->record_cnt = lex_integer (lexer);
178           lex_get (lexer);
179           lex_match (lexer, T_RPAREN);
180         }
181       else if (lex_match_id (lexer, "TABLE"))
182         print_table = true;
183       else if (lex_match_id (lexer, "NOTABLE"))
184         print_table = false;
185       else
186         {
187           lex_error (lexer, _("expecting a valid subcommand"));
188           goto error;
189         }
190     }
191
192   /* When PRINT or PRINT EJECT writes to an external file, we
193      prefix each line with a space for compatibility. */
194   trns->include_prefix = which_formats == PRINT && fh != NULL;
195
196   /* Parse variables and strings. */
197   if (!parse_specs (lexer, tmp_pool, trns, dataset_dict (ds), which_formats))
198     goto error;
199
200   /* Are there any binary formats?
201
202      There are real difficulties figuring out what to do when both binary
203      formats and nontrivial encodings enter the picture.  So when binary
204      formats are present we fall back to much simpler handling. */
205   binary = false;
206   ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
207     {
208       if (spec->type == PRT_VAR
209           && fmt_get_category (spec->format.type) == FMT_CAT_BINARY)
210         {
211           binary = true;
212           break;
213         }
214     }
215   if (binary && fh == NULL)
216     {
217       msg (SE, _("%s is required when binary formats are specified."), "OUTFILE");
218       goto error;
219     }
220
221   if (lex_end_of_command (lexer) != CMD_SUCCESS)
222     goto error;
223
224   if (fh != NULL)
225     {
226       trns->writer = dfm_open_writer (fh, encoding);
227       if (trns->writer == NULL)
228         goto error;
229       trns->encoding = dfm_writer_get_encoding (trns->writer);
230     }
231   else
232     trns->encoding = UTF8;
233
234   /* Output the variable table if requested. */
235   if (print_table)
236     dump_table (trns, fh);
237
238   /* Put the transformation in the queue. */
239   add_transformation (ds,
240                       (binary
241                        ? print_binary_trns_proc
242                        : print_text_trns_proc),
243                       print_trns_free, trns);
244
245   pool_destroy (tmp_pool);
246   fh_unref (fh);
247
248   return CMD_SUCCESS;
249
250  error:
251   print_trns_free (trns);
252   fh_unref (fh);
253   return CMD_FAILURE;
254 }
255 \f
256 static bool parse_string_argument (struct lexer *, struct print_trns *,
257                                    int record, int *column);
258 static bool parse_variable_argument (struct lexer *, const struct dictionary *,
259                                      struct print_trns *,
260                                      struct pool *tmp_pool,
261                                      int *record, int *column,
262                                      enum which_formats);
263
264 /* Parses all the variable and string specifications on a single
265    PRINT, PRINT EJECT, or WRITE command into the prt structure.
266    Returns success. */
267 static bool
268 parse_specs (struct lexer *lexer, struct pool *tmp_pool, struct print_trns *trns,
269              struct dictionary *dict,
270              enum which_formats which_formats)
271 {
272   int record = 0;
273   int column = 1;
274
275   if (lex_token (lexer) == T_ENDCMD)
276     {
277       trns->record_cnt = 1;
278       return true;
279     }
280
281   while (lex_token (lexer) != T_ENDCMD)
282     {
283       bool ok;
284
285       if (!parse_record_placement (lexer, &record, &column))
286         return false;
287
288       if (lex_is_string (lexer))
289         ok = parse_string_argument (lexer, trns, record, &column);
290       else
291         ok = parse_variable_argument (lexer, dict, trns, tmp_pool, &record,
292                                       &column, which_formats);
293       if (!ok)
294         return 0;
295
296       lex_match (lexer, T_COMMA);
297     }
298
299   if (trns->record_cnt != 0 && trns->record_cnt != record)
300     msg (SW, _("Output calls for %d records but %zu specified on RECORDS "
301                "subcommand."),
302          record, trns->record_cnt);
303   trns->record_cnt = record;
304
305   return true;
306 }
307
308 /* Parses a string argument to the PRINT commands.  Returns success. */
309 static bool
310 parse_string_argument (struct lexer *lexer, struct print_trns *trns, int record, int *column)
311 {
312   struct prt_out_spec *spec = pool_alloc (trns->pool, sizeof *spec);
313   spec->type = PRT_LITERAL;
314   spec->record = record;
315   spec->first_column = *column;
316   ds_init_substring (&spec->string, lex_tokss (lexer));
317   ds_register_pool (&spec->string, trns->pool);
318   lex_get (lexer);
319
320   /* Parse the included column range. */
321   if (lex_is_number (lexer))
322     {
323       int first_column, last_column;
324       bool range_specified;
325
326       if (!parse_column_range (lexer, 1,
327                                &first_column, &last_column, &range_specified))
328         return false;
329
330       spec->first_column = first_column;
331       if (range_specified)
332         ds_set_length (&spec->string, last_column - first_column + 1, ' ');
333     }
334
335   spec->width = u8_strwidth (CHAR_CAST (const uint8_t *,
336                                         ds_cstr (&spec->string)),
337                              UTF8);
338   *column = spec->first_column + spec->width;
339
340   ll_push_tail (&trns->specs, &spec->ll);
341   return true;
342 }
343
344 /* Parses a variable argument to the PRINT commands by passing it off
345    to fixed_parse_compatible() or fixed_parse_fortran() as appropriate.
346    Returns success. */
347 static bool
348 parse_variable_argument (struct lexer *lexer, const struct dictionary *dict,
349                          struct print_trns *trns, struct pool *tmp_pool,
350                          int *record, int *column,
351                          enum which_formats which_formats)
352 {
353   const struct variable **vars;
354   size_t var_cnt, var_idx;
355   struct fmt_spec *formats, *f;
356   size_t format_cnt;
357   bool add_space;
358
359   if (!parse_variables_const_pool (lexer, tmp_pool, dict,
360                              &vars, &var_cnt, PV_DUPLICATE))
361     return false;
362
363   if (lex_is_number (lexer) || lex_token (lexer) == T_LPAREN)
364     {
365       if (!parse_var_placements (lexer, tmp_pool, var_cnt, FMT_FOR_OUTPUT,
366                                  &formats, &format_cnt))
367         return false;
368       add_space = false;
369     }
370   else
371     {
372       size_t i;
373
374       lex_match (lexer, T_ASTERISK);
375
376       formats = pool_nmalloc (tmp_pool, var_cnt, sizeof *formats);
377       format_cnt = var_cnt;
378       for (i = 0; i < var_cnt; i++)
379         {
380           const struct variable *v = vars[i];
381           formats[i] = (which_formats == PRINT
382                         ? *var_get_print_format (v)
383                         : *var_get_write_format (v));
384         }
385       add_space = which_formats == PRINT;
386     }
387
388   var_idx = 0;
389   for (f = formats; f < &formats[format_cnt]; f++)
390     if (!execute_placement_format (f, record, column))
391       {
392         const struct variable *var;
393         struct prt_out_spec *spec;
394
395         var = vars[var_idx++];
396         if (!fmt_check_width_compat (f, var_get_width (var)))
397           return false;
398
399         spec = pool_alloc (trns->pool, sizeof *spec);
400         spec->type = PRT_VAR;
401         spec->record = *record;
402         spec->first_column = *column;
403         spec->var = var;
404         spec->format = *f;
405         spec->add_space = add_space;
406
407         /* This is a completely bizarre twist for compatibility:
408            WRITE outputs the system-missing value as a field
409            filled with spaces, instead of using the normal format
410            that usually contains a period. */
411         spec->sysmis_as_spaces = (which_formats == WRITE
412                                   && var_is_numeric (var)
413                                   && (fmt_get_category (spec->format.type)
414                                       != FMT_CAT_BINARY));
415
416         ll_push_tail (&trns->specs, &spec->ll);
417
418         *column += f->w + add_space;
419       }
420   assert (var_idx == var_cnt);
421
422   return true;
423 }
424
425 /* Prints the table produced by the TABLE subcommand to the listing
426    file. */
427 static void
428 dump_table (struct print_trns *trns, const struct file_handle *fh)
429 {
430   struct prt_out_spec *spec;
431   struct tab_table *t;
432   int spec_cnt;
433   int row;
434
435   spec_cnt = ll_count (&trns->specs);
436   t = tab_create (4, spec_cnt + 1);
437   tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 3, spec_cnt);
438   tab_hline (t, TAL_2, 0, 3, 1);
439   tab_headers (t, 0, 0, 1, 0);
440   tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Variable"));
441   tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Record"));
442   tab_text (t, 2, 0, TAB_CENTER | TAT_TITLE, _("Columns"));
443   tab_text (t, 3, 0, TAB_CENTER | TAT_TITLE, _("Format"));
444   row = 1;
445   ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
446     {
447       char fmt_string[FMT_STRING_LEN_MAX + 1];
448       int width;
449       switch (spec->type)
450         {
451         case PRT_LITERAL:
452           tab_text_format (t, 0, row, TAB_LEFT | TAB_FIX, "`%.*s'",
453                            (int) ds_length (&spec->string),
454                            ds_data (&spec->string));
455           width = ds_length (&spec->string);
456           break;
457         case PRT_VAR:
458           tab_text (t, 0, row, TAB_LEFT, var_get_name (spec->var));
459           tab_text (t, 3, row, TAB_LEFT | TAB_FIX,
460                     fmt_to_string (&spec->format, fmt_string));
461           width = spec->format.w;
462           break;
463         default:
464           NOT_REACHED ();
465         }
466       tab_text_format (t, 1, row, 0, "%d", spec->record);
467       tab_text_format (t, 2, row, 0, "%3d-%3d",
468                        spec->first_column, spec->first_column + width - 1);
469       row++;
470     }
471
472   if (fh != NULL)
473     tab_title (t, ngettext ("Writing %zu record to %s.",
474                             "Writing %zu records to %s.", trns->record_cnt),
475                trns->record_cnt, fh_get_name (fh));
476   else
477     tab_title (t, ngettext ("Writing %zu record.",
478                             "Writing %zu records.", trns->record_cnt),
479                trns->record_cnt);
480   tab_submit (t);
481 }
482 \f
483 /* Transformation, for all-text output. */
484
485 static void print_text_flush_records (struct print_trns *, struct u8_line *,
486                                       int target_record,
487                                       bool *eject, int *record);
488
489 /* Performs the transformation inside print_trns T on case C. */
490 static int
491 print_text_trns_proc (void *trns_, struct ccase **c,
492                       casenumber case_num UNUSED)
493 {
494   struct print_trns *trns = trns_;
495   struct prt_out_spec *spec;
496   struct u8_line line;
497
498   bool eject = trns->eject;
499   int record = 1;
500
501   u8_line_init (&line);
502   ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
503     {
504       int x0 = spec->first_column;
505
506       print_text_flush_records (trns, &line, spec->record, &eject, &record);
507
508       u8_line_set_length (&line, spec->first_column);
509       if (spec->type == PRT_VAR)
510         {
511           const union value *input = case_data (*c, spec->var);
512           int x1;
513
514           if (!spec->sysmis_as_spaces || input->f != SYSMIS)
515             {
516               size_t len;
517               int width;
518               char *s;
519
520               s = data_out (input, var_get_encoding (spec->var),
521                             &spec->format);
522               len = strlen (s);
523               width = u8_width (CHAR_CAST (const uint8_t *, s), len, UTF8);
524               x1 = x0 + width;
525               u8_line_put (&line, x0, x1, s, len);
526               free (s);
527             }
528           else
529             {
530               int n = spec->format.w;
531
532               x1 = x0 + n;
533               memset (u8_line_reserve (&line, x0, x1, n), ' ', n);
534             }
535
536           if (spec->add_space)
537             *u8_line_reserve (&line, x1, x1 + 1, 1) = ' ';
538         }
539       else
540         {
541           const struct string *s = &spec->string;
542
543           u8_line_put (&line, x0, x0 + spec->width,
544                        ds_data (s), ds_length (s));
545         }
546     }
547   print_text_flush_records (trns, &line, trns->record_cnt + 1,
548                             &eject, &record);
549   u8_line_destroy (&line);
550
551   if (trns->writer != NULL && dfm_write_error (trns->writer))
552     return TRNS_ERROR;
553   return TRNS_CONTINUE;
554 }
555
556 /* Advance from *RECORD to TARGET_RECORD, outputting records
557    along the way.  If *EJECT is true, then the first record
558    output is preceded by ejecting the page (and *EJECT is set
559    false). */
560 static void
561 print_text_flush_records (struct print_trns *trns, struct u8_line *line,
562                           int target_record, bool *eject, int *record)
563 {
564   for (; target_record > *record; (*record)++)
565     {
566       char leader = ' ';
567
568       if (*eject)
569         {
570           *eject = false;
571           if (trns->writer == NULL)
572             text_item_submit (text_item_create (TEXT_ITEM_EJECT_PAGE, ""));
573           else
574             leader = '1';
575         }
576       *u8_line_reserve (line, 0, 1, 1) = leader;
577
578       if (trns->writer == NULL)
579         tab_output_text (TAB_FIX, ds_cstr (&line->s) + 1);
580       else
581         {
582           size_t len = ds_length (&line->s);
583           char *s = ds_cstr (&line->s);
584
585           if (!trns->include_prefix)
586             {
587               s++;
588               len--;
589             }
590
591           if (is_encoding_utf8 (trns->encoding))
592             dfm_put_record (trns->writer, s, len);
593           else
594             {
595               char *recoded = recode_string (trns->encoding, UTF8, s, len);
596               dfm_put_record (trns->writer, recoded, strlen (recoded));
597               free (recoded);
598             }
599         }
600     }
601 }
602 \f
603 /* Transformation, for output involving binary. */
604
605 static void print_binary_flush_records (struct print_trns *,
606                                         struct string *line, int target_record,
607                                         bool *eject, int *record);
608
609 /* Performs the transformation inside print_trns T on case C. */
610 static int
611 print_binary_trns_proc (void *trns_, struct ccase **c,
612                         casenumber case_num UNUSED)
613 {
614   struct print_trns *trns = trns_;
615   bool eject = trns->eject;
616   char encoded_space = recode_byte (trns->encoding, C_ENCODING, ' ');
617   int record = 1;
618   struct prt_out_spec *spec;
619   struct string line;
620
621   ds_init_empty (&line);
622   ds_put_byte (&line, ' ');
623   ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
624     {
625       print_binary_flush_records (trns, &line, spec->record, &eject, &record);
626
627       ds_set_length (&line, spec->first_column, encoded_space);
628       if (spec->type == PRT_VAR)
629         {
630           const union value *input = case_data (*c, spec->var);
631           if (!spec->sysmis_as_spaces || input->f != SYSMIS)
632             data_out_recode (input, var_get_encoding (spec->var),
633                              &spec->format, &line, trns->encoding);
634           else
635             ds_put_byte_multiple (&line, encoded_space, spec->format.w);
636           if (spec->add_space)
637             ds_put_byte (&line, encoded_space);
638         }
639       else
640         {
641           ds_put_substring (&line, ds_ss (&spec->string));
642           if (0 != strcmp (trns->encoding, UTF8))
643             {
644               size_t length = ds_length (&spec->string);
645               char *data = ss_data (ds_tail (&line, length));
646               char *s = recode_string (trns->encoding, UTF8, data, length);
647               memcpy (data, s, length);
648               free (s);
649             }
650         }
651     }
652   print_binary_flush_records (trns, &line, trns->record_cnt + 1,
653                               &eject, &record);
654   ds_destroy (&line);
655
656   if (trns->writer != NULL && dfm_write_error (trns->writer))
657     return TRNS_ERROR;
658   return TRNS_CONTINUE;
659 }
660
661 /* Advance from *RECORD to TARGET_RECORD, outputting records
662    along the way.  If *EJECT is true, then the first record
663    output is preceded by ejecting the page (and *EJECT is set
664    false). */
665 static void
666 print_binary_flush_records (struct print_trns *trns, struct string *line,
667                             int target_record, bool *eject, int *record)
668 {
669   for (; target_record > *record; (*record)++)
670     {
671       char *s = ds_cstr (line);
672       size_t length = ds_length (line);
673       char leader = ' ';
674
675       if (*eject)
676         {
677           *eject = false;
678           leader = '1';
679         }
680       s[0] = recode_byte (trns->encoding, C_ENCODING, leader);
681
682       if (!trns->include_prefix)
683         {
684           s++;
685           length--;
686         }
687       dfm_put_record (trns->writer, s, length);
688
689       ds_truncate (line, 1);
690     }
691 }
692 \f
693 /* Frees TRNS. */
694 static bool
695 print_trns_free (void *trns_)
696 {
697   struct print_trns *trns = trns_;
698   bool ok = true;
699
700   if (trns->writer != NULL)
701     ok = dfm_close_writer (trns->writer);
702   pool_destroy (trns->pool);
703
704   return ok;
705 }
706