refactoring
[pspp] / src / language / data-io / data-reader.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-2004, 2006, 2010, 2011, 2012, 2016 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 "language/data-io/data-reader.h"
20
21 #include <ctype.h>
22 #include <errno.h>
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26
27 #include "data/casereader.h"
28 #include "data/dataset.h"
29 #include "data/file-handle-def.h"
30 #include "data/file-name.h"
31 #include "language/command.h"
32 #include "language/data-io/file-handle.h"
33 #include "language/lexer/lexer.h"
34 #include "libpspp/assertion.h"
35 #include "libpspp/cast.h"
36 #include "libpspp/encoding-guesser.h"
37 #include "libpspp/integer-format.h"
38 #include "libpspp/line-reader.h"
39 #include "libpspp/message.h"
40 #include "libpspp/str.h"
41
42 #include "gl/minmax.h"
43 #include "gl/xalloc.h"
44
45 #include "gettext.h"
46 #define _(msgid) gettext (msgid)
47 #define N_(msgid) (msgid)
48
49 /* Flags for DFM readers. */
50 enum dfm_reader_flags
51   {
52     DFM_ADVANCE = 002,          /* Read next line on dfm_get_record() call? */
53     DFM_SAW_BEGIN_DATA = 004,   /* For inline_file only, whether we've
54                                    already read a BEGIN DATA line. */
55     DFM_TABS_EXPANDED = 010,    /* Tabs have been expanded. */
56     DFM_CONSUME = 020           /* read_inline_record() should get a token? */
57   };
58
59 /* Data file reader. */
60 struct dfm_reader
61   {
62     struct file_handle *fh;     /* File handle. */
63     struct fh_lock *lock;       /* Mutual exclusion lock for file. */
64     int line_number;            /* Current line or record number. */
65     struct string line;         /* Current line. */
66     struct string scratch;      /* Extra line buffer. */
67     enum dfm_reader_flags flags; /* Zero or more of DFM_*. */
68     FILE *file;                 /* Associated file. */
69     size_t pos;                 /* Offset in line of current character. */
70     unsigned n_eofs;            /* # of attempts to advance past EOF. */
71     struct lexer *lexer;        /* The lexer reading the file */
72     char *encoding;             /* Current encoding. */
73
74     /* For FH_MODE_TEXT only. */
75     struct line_reader *line_reader;
76
77     /* For FH_MODE_360_VARIABLE and FH_MODE_360_SPANNED files only. */
78     size_t block_left;          /* Bytes left in current block. */
79   };
80
81 /* Closes reader R opened by dfm_open_reader(). */
82 void
83 dfm_close_reader (struct dfm_reader *r)
84 {
85   if (r == NULL)
86     return;
87
88   if (fh_unlock (r->lock))
89     {
90       /* File is still locked by another client. */
91       return;
92     }
93
94   /* This was the last client, so close the underlying file. */
95   if (fh_get_referent (r->fh) != FH_REF_INLINE)
96     fn_close (r->fh, r->file);
97   else
98     {
99       /* Skip any remaining data on the inline file. */
100       if (r->flags & DFM_SAW_BEGIN_DATA)
101         {
102           dfm_reread_record (r, 0);
103           while (!dfm_eof (r))
104             dfm_forward_record (r);
105         }
106     }
107
108   line_reader_free (r->line_reader);
109   free (r->encoding);
110   fh_unref (r->fh);
111   ds_destroy (&r->line);
112   ds_destroy (&r->scratch);
113   free (r);
114 }
115
116 /* Opens the file designated by file handle FH for reading as a data file.
117    Returns a reader if successful, or a null pointer otherwise.
118
119    If FH is fh_inline_file() then the new reader reads data included inline in
120    the command file between BEGIN FILE and END FILE, obtaining data from LEXER.
121    LEXER must remain valid as long as the new reader is in use.  ENCODING is
122    ignored.
123
124    If FH is not fh_inline_file(), then the encoding of the file read is by
125    default that of FH itself.  If ENCODING is nonnull, then it overrides the
126    default encoding.  LEXER is ignored. */
127 struct dfm_reader *
128 dfm_open_reader (struct file_handle *fh, struct lexer *lexer,
129                  const char *encoding)
130 {
131   struct dfm_reader *r;
132   struct fh_lock *lock;
133
134   /* TRANSLATORS: this fragment will be interpolated into
135      messages in fh_lock() that identify types of files. */
136   lock = fh_lock (fh, FH_REF_FILE | FH_REF_INLINE, N_("data file"),
137                   FH_ACC_READ, false);
138   if (lock == NULL)
139     return NULL;
140
141   r = fh_lock_get_aux (lock);
142   if (r != NULL)
143     return r;
144
145   r = xmalloc (sizeof *r);
146   r->fh = fh_ref (fh);
147   r->lock = lock;
148   r->lexer = lexer;
149   ds_init_empty (&r->line);
150   ds_init_empty (&r->scratch);
151   r->flags = DFM_ADVANCE;
152   r->n_eofs = 0;
153   r->block_left = 0;
154   if (fh_get_referent (fh) != FH_REF_INLINE)
155     {
156       r->line_number = 0;
157       r->file = fn_open (fh, "rb");
158       if (r->file == NULL)
159         {
160           msg (ME, _("Could not open `%s' for reading as a data file: %s."),
161                fh_get_file_name (r->fh), strerror (errno));
162           goto error;
163         }
164     }
165   fh_lock_set_aux (lock, r);
166
167   if (encoding == NULL)
168     encoding = fh_get_encoding (fh);
169   if (fh_get_referent (fh) == FH_REF_FILE && fh_get_mode (fh) == FH_MODE_TEXT)
170     {
171       r->line_reader = line_reader_for_fd (encoding, fileno (r->file));
172       if (r->line_reader == NULL)
173         {
174           msg (ME, _("Could not read `%s' as a text file with encoding `%s': "
175                      "%s."),
176                fh_get_file_name (r->fh), encoding, strerror (errno));
177           goto error;
178         }
179       r->encoding = xstrdup (line_reader_get_encoding (r->line_reader));
180     }
181   else
182     {
183       r->line_reader = NULL;
184       r->encoding = xstrdup (encoding_guess_parse_encoding (encoding));
185     }
186
187   return r;
188
189 error:
190   fh_unlock (r->lock);
191   fh_unref (fh);
192   free (r);
193   return NULL;
194 }
195
196 /* Returns true if an I/O error occurred on READER, false otherwise. */
197 bool
198 dfm_reader_error (const struct dfm_reader *r)
199 {
200   return (fh_get_referent (r->fh) == FH_REF_FILE
201           && (r->line_reader != NULL
202               ? line_reader_error (r->line_reader) != 0
203               : ferror (r->file)));
204 }
205
206 /* Reads a record from the inline file into R.
207    Returns true if successful, false on failure. */
208 static bool
209 read_inline_record (struct dfm_reader *r)
210 {
211   if ((r->flags & DFM_SAW_BEGIN_DATA) == 0)
212     {
213       r->flags |= DFM_SAW_BEGIN_DATA;
214       r->flags &= ~DFM_CONSUME;
215
216       while (lex_token (r->lexer) == T_ENDCMD)
217         lex_get (r->lexer);
218
219       if (!lex_force_match_id (r->lexer, "BEGIN")
220           || !lex_force_match_id (r->lexer, "DATA"))
221         return false;
222
223       lex_match (r->lexer, T_ENDCMD);
224     }
225
226   if (r->flags & DFM_CONSUME)
227     lex_get (r->lexer);
228
229   if (!lex_is_string (r->lexer))
230     {
231       if (!lex_match_id (r->lexer, "END") || !lex_match_id (r->lexer, "DATA"))
232         {
233           msg (SE, _("Missing %s while reading inline data.  "
234                      "This probably indicates a missing or incorrectly "
235                      "formatted %s command.  %s must appear "
236                      "by itself on a single line with exactly one space "
237                      "between words."), "END DATA", "END DATA", "END DATA");
238           lex_discard_rest_of_command (r->lexer);
239         }
240       return false;
241     }
242
243   ds_assign_substring (&r->line, lex_tokss (r->lexer));
244   r->flags |= DFM_CONSUME;
245
246   return true;
247 }
248
249 /* Report a read error on R. */
250 static void
251 read_error (struct dfm_reader *r)
252 {
253   msg (ME, _("Error reading file %s: %s."),
254        fh_get_name (r->fh), strerror (errno));
255 }
256
257 /* Report a partial read at end of file reading R. */
258 static void
259 partial_record (struct dfm_reader *r)
260 {
261   msg (ME, _("Unexpected end of file in partial record reading %s."),
262        fh_get_name (r->fh));
263 }
264
265 /* Tries to read SIZE bytes from R into BUFFER.  Returns 1 if
266    successful, 0 if end of file was reached before any bytes
267    could be read, and -1 if some bytes were read but fewer than
268    SIZE due to end of file or an error mid-read.  In the latter
269    case, reports an error. */
270 static int
271 try_to_read_fully (struct dfm_reader *r, void *buffer, size_t size)
272 {
273   size_t bytes_read = fread (buffer, 1, size, r->file);
274   if (bytes_read == size)
275     return 1;
276   else if (bytes_read == 0)
277     return 0;
278   else
279     {
280       partial_record (r);
281       return -1;
282     }
283 }
284
285 /* Type of a descriptor word. */
286 enum descriptor_type
287   {
288     BLOCK,
289     RECORD
290   };
291
292 /* Reads a block descriptor word or record descriptor word
293    (according to TYPE) from R.  Returns 1 if successful, 0 if
294    end of file was reached before any bytes could be read, -1 if
295    an error occurred.  Reports an error in the latter case.
296
297    If successful, stores the number of remaining bytes in the
298    block or record (that is, the block or record length, minus
299    the 4 bytes in the BDW or RDW itself) into *REMAINING_SIZE.
300    If SEGMENT is nonnull, also stores the segment control
301    character (SCC) into *SEGMENT. */
302 static int
303 read_descriptor_word (struct dfm_reader *r, enum descriptor_type type,
304                       size_t *remaining_size, int *segment)
305 {
306   uint8_t raw_descriptor[4];
307   int status;
308
309   status = try_to_read_fully (r, raw_descriptor, sizeof raw_descriptor);
310   if (status <= 0)
311     return status;
312
313   *remaining_size = (raw_descriptor[0] << 8) | raw_descriptor[1];
314   if (segment != NULL)
315     *segment = raw_descriptor[2];
316
317   if (*remaining_size < 4)
318     {
319       msg (ME,
320            (type == BLOCK
321             ? _("Corrupt block descriptor word at offset 0x%lx in %s.")
322             : _("Corrupt record descriptor word at offset 0x%lx in %s.")),
323            (long) ftello (r->file) - 4, fh_get_name (r->fh));
324       return -1;
325     }
326
327   *remaining_size -= 4;
328   return 1;
329 }
330
331 /* Reports that reader R has read a corrupt record size. */
332 static void
333 corrupt_size (struct dfm_reader *r)
334 {
335   msg (ME, _("Corrupt record size at offset 0x%lx in %s."),
336        (long) ftello (r->file) - 4, fh_get_name (r->fh));
337 }
338
339 /* Reads a 32-byte little-endian signed number from R and stores
340    its value into *SIZE_OUT.  Returns 1 if successful, 0 if end
341    of file was reached before any bytes could be read, -1 if an
342    error occurred.  Reports an error in the latter case.  Numbers
343    less than 0 are considered errors. */
344 static int
345 read_size (struct dfm_reader *r, size_t *size_out)
346 {
347   int32_t size;
348   int status;
349
350   status = try_to_read_fully (r, &size, sizeof size);
351   if (status <= 0)
352     return status;
353
354   integer_convert (INTEGER_LSB_FIRST, &size, INTEGER_NATIVE, &size,
355                    sizeof size);
356   if (size < 0)
357     {
358       corrupt_size (r);
359       return -1;
360     }
361
362   *size_out = size;
363   return 1;
364 }
365
366 static bool
367 read_text_record (struct dfm_reader *r)
368 {
369   bool is_auto;
370   bool ok;
371
372   /* Read a line.  If the line reader's encoding changes, update r->encoding to
373      match. */
374   is_auto = line_reader_is_auto (r->line_reader);
375   ok = line_reader_read (r->line_reader, &r->line, SIZE_MAX);
376   if (is_auto && !line_reader_is_auto (r->line_reader))
377     {
378       free (r->encoding);
379       r->encoding = xstrdup (line_reader_get_encoding (r->line_reader));
380     }
381
382   /* Detect and report read error. */
383   if (!ok)
384     {
385       int error = line_reader_error (r->line_reader);
386       if (error != 0)
387         msg (ME, _("Error reading file %s: %s."),
388              fh_get_name (r->fh), strerror (error));
389     }
390
391   return ok;
392 }
393
394 /* Reads a record from a disk file into R.
395    Returns true if successful, false on error or at end of file. */
396 static bool
397 read_file_record (struct dfm_reader *r)
398 {
399   assert (r->fh != fh_inline_file ());
400
401   ds_clear (&r->line);
402   switch (fh_get_mode (r->fh))
403     {
404     case FH_MODE_TEXT:
405       return read_text_record (r);
406
407     case FH_MODE_FIXED:
408       if (ds_read_stream (&r->line, 1, fh_get_record_width (r->fh), r->file))
409         return true;
410       else
411         {
412           if (ferror (r->file))
413             read_error (r);
414           else if (!ds_is_empty (&r->line))
415             partial_record (r);
416           return false;
417         }
418
419     case FH_MODE_VARIABLE:
420       {
421         size_t leading_size;
422         size_t trailing_size;
423         int status;
424
425         /* Read leading record size. */
426         status = read_size (r, &leading_size);
427         if (status <= 0)
428           return false;
429
430         /* Read record data. */
431         if (!ds_read_stream (&r->line, leading_size, 1, r->file))
432           {
433             if (ferror (r->file))
434               read_error (r);
435             else
436               partial_record (r);
437             return false;
438           }
439
440         /* Read trailing record size and check that it's the same
441            as the leading record size. */
442         status = read_size (r, &trailing_size);
443         if (status <= 0)
444           {
445             if (status == 0)
446               partial_record (r);
447             return false;
448           }
449         if (leading_size != trailing_size)
450           {
451             corrupt_size (r);
452             return false;
453           }
454
455         return true;
456       }
457
458     case FH_MODE_360_VARIABLE:
459     case FH_MODE_360_SPANNED:
460       for (;;)
461         {
462           size_t record_size;
463           int segment;
464           int status;
465
466           /* If we've exhausted our current block, start another
467              one by reading the new block descriptor word. */
468           if (r->block_left == 0)
469             {
470               status = read_descriptor_word (r, BLOCK, &r->block_left, NULL);
471               if (status < 0)
472                 return false;
473               else if (status == 0)
474                 return !ds_is_empty (&r->line);
475             }
476
477           /* Read record descriptor. */
478           if (r->block_left < 4)
479             {
480               partial_record (r);
481               return false;
482             }
483           r->block_left -= 4;
484           status = read_descriptor_word (r, RECORD, &record_size, &segment);
485           if (status <= 0)
486             {
487               if (status == 0)
488                 partial_record (r);
489               return false;
490             }
491           if (record_size > r->block_left)
492             {
493               msg (ME, _("Record exceeds remaining block length."));
494               return false;
495             }
496
497           /* Read record data. */
498           if (!ds_read_stream (&r->line, record_size, 1, r->file))
499             {
500               if (ferror (r->file))
501                 read_error (r);
502               else
503                 partial_record (r);
504               return false;
505             }
506           r->block_left -= record_size;
507
508           /* In variable mode, read only a single record.
509              In spanned mode, a segment value of 0 should
510              designate a whole record without spanning, 1 the
511              first segment in a record, 2 the last segment in a
512              record, and 3 an intermediate segment in a record.
513              For compatibility, though, we actually pay attention
514              only to whether the segment value is even or odd. */
515           if (fh_get_mode (r->fh) == FH_MODE_360_VARIABLE
516               || (segment & 1) == 0)
517             return true;
518         }
519     }
520
521   NOT_REACHED ();
522 }
523
524 /* Reads a record from R, setting the current position to the
525    start of the line.  If an error occurs or end-of-file is
526    encountered, the current line is set to null. */
527 static bool
528 read_record (struct dfm_reader *r)
529 {
530   if (fh_get_referent (r->fh) == FH_REF_FILE)
531     {
532       bool ok = read_file_record (r);
533       if (ok)
534         r->line_number++;
535       return ok;
536     }
537   else
538     return read_inline_record (r);
539 }
540
541 /* Returns the number of attempts, thus far, to advance past
542    end-of-file in reader R.  Reads forward in HANDLE's file, if
543    necessary, to find out.
544
545    Normally, the user stops attempting to read from the file the
546    first time EOF is reached (a return value of 1).  If the user
547    tries to read past EOF again (a return value of 2 or more),
548    an error message is issued, and the caller should more
549    forcibly abort to avoid an infinite loop. */
550 unsigned
551 dfm_eof (struct dfm_reader *r)
552 {
553   if (r->flags & DFM_ADVANCE)
554     {
555       r->flags &= ~DFM_ADVANCE;
556
557       if (r->n_eofs == 0 && read_record (r))
558         {
559           r->pos = 0;
560           return 0;
561         }
562
563       r->n_eofs++;
564       if (r->n_eofs == 2)
565         {
566           if (r->fh != fh_inline_file ())
567             msg (ME, _("Attempt to read beyond end-of-file on file %s."),
568                  fh_get_name (r->fh));
569           else
570             msg (ME, _("Attempt to read beyond %s."), "END DATA");
571         }
572     }
573
574   return r->n_eofs;
575 }
576
577 /* Returns the current record in the file corresponding to
578    HANDLE.  Aborts if reading from the file is necessary or at
579    end of file, so call dfm_eof() first. */
580 struct substring
581 dfm_get_record (struct dfm_reader *r)
582 {
583   assert ((r->flags & DFM_ADVANCE) == 0);
584   assert (r->n_eofs == 0);
585
586   return ds_substr (&r->line, r->pos, SIZE_MAX);
587 }
588
589 /* Expands tabs in the current line into the equivalent number of
590    spaces, if appropriate for this kind of file.  Aborts if
591    reading from the file is necessary or at end of file, so call
592    dfm_eof() first.*/
593 void
594 dfm_expand_tabs (struct dfm_reader *r)
595 {
596   size_t ofs, new_pos, tab_width;
597
598   assert ((r->flags & DFM_ADVANCE) == 0);
599   assert (r->n_eofs == 0);
600
601   if (r->flags & DFM_TABS_EXPANDED)
602     return;
603   r->flags |= DFM_TABS_EXPANDED;
604
605   if (r->fh != fh_inline_file ()
606       && (fh_get_mode (r->fh) != FH_MODE_TEXT
607           || fh_get_tab_width (r->fh) == 0
608           || ds_find_byte (&r->line, '\t') == SIZE_MAX))
609     return;
610
611   /* Expand tabs from r->line into r->scratch, and figure out
612      new value for r->pos. */
613   tab_width = fh_get_tab_width (r->fh);
614   ds_clear (&r->scratch);
615   new_pos = SIZE_MAX;
616   for (ofs = 0; ofs < ds_length (&r->line); ofs++)
617     {
618       unsigned char c;
619
620       if (ofs == r->pos)
621         new_pos = ds_length (&r->scratch);
622
623       c = ds_data (&r->line)[ofs];
624       if (c != '\t')
625         ds_put_byte (&r->scratch, c);
626       else
627         {
628           do
629             ds_put_byte (&r->scratch, ' ');
630           while (ds_length (&r->scratch) % tab_width != 0);
631         }
632     }
633   if (new_pos == SIZE_MAX)
634     {
635       /* Maintain the same relationship between position and line
636          length that we had before.  DATA LIST uses a
637          beyond-the-end position to deal with an empty field at
638          the end of the line. */
639       assert (r->pos >= ds_length (&r->line));
640       new_pos = (r->pos - ds_length (&r->line)) + ds_length (&r->scratch);
641     }
642
643   /* Swap r->line and r->scratch and set new r->pos. */
644   ds_swap (&r->line, &r->scratch);
645   r->pos = new_pos;
646 }
647
648 /* Returns the character encoding of data read from READER. */
649 const char *
650 dfm_reader_get_encoding (const struct dfm_reader *reader)
651 {
652   return reader->encoding;
653 }
654
655 /* Causes dfm_get_record() or dfm_get_whole_record() to read in
656    the next record the next time it is executed on file
657    HANDLE. */
658 void
659 dfm_forward_record (struct dfm_reader *r)
660 {
661   r->flags |= DFM_ADVANCE;
662 }
663
664 /* Cancels the effect of any previous dfm_fwd_record() executed
665    on file HANDLE.  Sets the current line to begin in the 1-based
666    column COLUMN.  */
667 void
668 dfm_reread_record (struct dfm_reader *r, size_t column)
669 {
670   r->flags &= ~DFM_ADVANCE;
671   r->pos = MAX (column, 1) - 1;
672 }
673
674 /* Sets the current line to begin COLUMNS characters following
675    the current start. */
676 void
677 dfm_forward_columns (struct dfm_reader *r, size_t columns)
678 {
679   dfm_reread_record (r, (r->pos + 1) + columns);
680 }
681
682 /* Returns the 1-based column to which the line pointer in HANDLE
683    is set.  Unless dfm_reread_record() or dfm_forward_columns()
684    have been called, this is 1. */
685 size_t
686 dfm_column_start (const struct dfm_reader *r)
687 {
688   return r->pos + 1;
689 }
690
691 /* Returns the number of columns we are currently beyond the end
692    of the line.  At or before end-of-line, this is 0; one column
693    after end-of-line, this is 1; and so on. */
694 size_t
695 dfm_columns_past_end (const struct dfm_reader *r)
696 {
697   return r->pos < ds_length (&r->line) ? 0 : ds_length (&r->line) - r->pos;
698 }
699
700 /* Returns the 1-based column within the current line that P
701    designates. */
702 size_t
703 dfm_get_column (const struct dfm_reader *r, const char *p)
704 {
705   return ds_pointer_to_position (&r->line, p) + 1;
706 }
707
708 const char *
709 dfm_get_file_name (const struct dfm_reader *r)
710 {
711   enum fh_referent referent = fh_get_referent (r->fh);
712   return (referent == FH_REF_FILE ? fh_get_file_name (r->fh)
713           : referent == FH_REF_INLINE ? lex_get_file_name (r->lexer)
714           : NULL);
715 }
716
717 int
718 dfm_get_line_number (const struct dfm_reader *r)
719 {
720   switch (fh_get_referent (r->fh))
721     {
722     case FH_REF_FILE:
723       return r->line_number;
724
725     case FH_REF_INLINE:
726       return lex_ofs_start_point (r->lexer, lex_ofs (r->lexer)).line;
727
728     case FH_REF_DATASET:
729     default:
730       return -1;
731     }
732 }
733 \f
734 /* BEGIN DATA...END DATA procedure. */
735
736 /* Perform BEGIN DATA...END DATA as a procedure in itself. */
737 int
738 cmd_begin_data (struct lexer *lexer, struct dataset *ds)
739 {
740   struct dfm_reader *r;
741   bool ok;
742
743   if (!fh_is_locked (fh_inline_file (), FH_ACC_READ))
744     {
745       msg (SE, _("This command is not valid here since the current "
746                  "input program does not access the inline file."));
747       return CMD_CASCADING_FAILURE;
748     }
749   lex_match (lexer, T_ENDCMD);
750
751   /* Open inline file. */
752   r = dfm_open_reader (fh_inline_file (), lexer, NULL);
753   r->flags |= DFM_SAW_BEGIN_DATA;
754   r->flags &= ~DFM_CONSUME;
755
756   /* Input procedure reads from inline file. */
757   casereader_destroy (proc_open (ds));
758   ok = proc_commit (ds);
759   dfm_close_reader (r);
760
761   return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
762 }