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