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 eof_cnt;           /* # 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
dfm_close_reader(struct dfm_reader * r)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 *
dfm_open_reader(struct file_handle * fh,struct lexer * lexer,const char * encoding)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->eof_cnt = 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
dfm_reader_error(const struct dfm_reader * r)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
read_inline_record(struct dfm_reader * r)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
read_error(struct dfm_reader * r)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
partial_record(struct dfm_reader * r)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
try_to_read_fully(struct dfm_reader * r,void * buffer,size_t size)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
read_descriptor_word(struct dfm_reader * r,enum descriptor_type type,size_t * remaining_size,int * segment)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
corrupt_size(struct dfm_reader * r)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
read_size(struct dfm_reader * r,size_t * size_out)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
read_text_record(struct dfm_reader * r)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
read_file_record(struct dfm_reader * r)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
read_record(struct dfm_reader * r)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
dfm_eof(struct dfm_reader * r)551 dfm_eof (struct dfm_reader *r)
552 {
553   if (r->flags & DFM_ADVANCE)
554     {
555       r->flags &= ~DFM_ADVANCE;
556 
557       if (r->eof_cnt == 0 && read_record (r))
558         {
559           r->pos = 0;
560           return 0;
561         }
562 
563       r->eof_cnt++;
564       if (r->eof_cnt == 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->eof_cnt;
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
dfm_get_record(struct dfm_reader * r)581 dfm_get_record (struct dfm_reader *r)
582 {
583   assert ((r->flags & DFM_ADVANCE) == 0);
584   assert (r->eof_cnt == 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
dfm_expand_tabs(struct dfm_reader * r)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->eof_cnt == 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 *
dfm_reader_get_encoding(const struct dfm_reader * reader)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
dfm_forward_record(struct dfm_reader * r)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
dfm_reread_record(struct dfm_reader * r,size_t column)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
dfm_forward_columns(struct dfm_reader * r,size_t columns)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
dfm_column_start(const struct dfm_reader * r)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
dfm_columns_past_end(const struct dfm_reader * r)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
dfm_get_column(const struct dfm_reader * r,const char * p)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 *
dfm_get_file_name(const struct dfm_reader * r)709 dfm_get_file_name (const struct dfm_reader *r)
710 {
711   return (fh_get_referent (r->fh) == FH_REF_FILE
712           ? fh_get_file_name (r->fh)
713           : NULL);
714 }
715 
716 int
dfm_get_line_number(const struct dfm_reader * r)717 dfm_get_line_number (const struct dfm_reader *r)
718 {
719   return fh_get_referent (r->fh) == FH_REF_FILE ? r->line_number : -1;
720 }
721 
722 /* BEGIN DATA...END DATA procedure. */
723 
724 /* Perform BEGIN DATA...END DATA as a procedure in itself. */
725 int
cmd_begin_data(struct lexer * lexer,struct dataset * ds)726 cmd_begin_data (struct lexer *lexer, struct dataset *ds)
727 {
728   struct dfm_reader *r;
729   bool ok;
730 
731   if (!fh_is_locked (fh_inline_file (), FH_ACC_READ))
732     {
733       msg (SE, _("This command is not valid here since the current "
734                  "input program does not access the inline file."));
735       return CMD_CASCADING_FAILURE;
736     }
737   lex_match (lexer, T_ENDCMD);
738 
739   /* Open inline file. */
740   r = dfm_open_reader (fh_inline_file (), lexer, NULL);
741   r->flags |= DFM_SAW_BEGIN_DATA;
742   r->flags &= ~DFM_CONSUME;
743 
744   /* Input procedure reads from inline file. */
745   casereader_destroy (proc_open (ds));
746   ok = proc_commit (ds);
747   dfm_close_reader (r);
748 
749   return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
750 }
751