1 /* PSPP - computes sample statistics.
2 Copyright (C) 1997-2004, 2006 Free Software Foundation, Inc.
3 Written by Ben Pfaff <blp@gnu.org>.
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
29 #include "file-handle.h"
30 #include "file-handle-def.h"
38 #define _(msgid) gettext (msgid)
40 #include "debug-print.h"
42 /* Flags for DFM readers. */
45 DFM_EOF = 001, /* At end-of-file? */
46 DFM_ADVANCE = 002, /* Read next line on dfm_get_record() call? */
47 DFM_SAW_BEGIN_DATA = 004, /* For inline_file only, whether we've
48 already read a BEGIN DATA line. */
49 DFM_TABS_EXPANDED = 010, /* Tabs have been expanded. */
52 /* Data file reader. */
55 struct file_handle *fh; /* File handle. */
56 struct file_locator where; /* Current location in data file. */
57 struct string line; /* Current line. */
58 struct string scratch; /* Extra line buffer. */
59 enum dfm_reader_flags flags; /* Zero or more of DFM_*. */
60 struct file_ext file; /* Associated file. */
61 size_t pos; /* Offset in line of current character. */
64 static void read_record (struct dfm_reader *r);
66 /* Closes reader R opened by dfm_open_reader(). */
68 dfm_close_reader (struct dfm_reader *r)
76 is_inline = r->fh == fh_inline_file ();
77 still_open = fh_close (r->fh, "data file", "rs");
83 fn_close_ext (&r->file);
84 free (r->file.filename);
85 r->file.filename = NULL;
89 /* Skip any remaining data on the inline file. */
90 if (r->flags & DFM_SAW_BEGIN_DATA)
91 while ((r->flags & DFM_EOF) == 0)
95 ds_destroy (&r->line);
96 ds_destroy (&r->scratch);
100 /* Opens the file designated by file handle FH for reading as a
101 data file. Providing fh_inline_file() for FH designates the
102 "inline file", that is, data included inline in the command
103 file between BEGIN FILE and END FILE. Returns a reader if
104 successful, or a null pointer otherwise. */
106 dfm_open_reader (struct file_handle *fh)
108 struct dfm_reader *r;
111 rp = fh_open (fh, FH_REF_FILE | FH_REF_INLINE, "data file", "rs");
117 r = xmalloc (sizeof *r);
119 ds_init (&r->line, 64);
120 ds_init (&r->scratch, 0);
121 r->flags = DFM_ADVANCE;
122 if (fh != fh_inline_file ())
124 r->where.filename = fh_get_filename (fh);
125 r->where.line_number = 0;
127 r->file.filename = xstrdup (fh_get_filename (r->fh));
130 r->file.sequence_no = NULL;
131 r->file.param = NULL;
132 r->file.postopen = NULL;
133 r->file.preclose = NULL;
134 if (!fn_open_ext (&r->file))
136 msg (ME, _("Could not open \"%s\" for reading as a data file: %s."),
137 fh_get_filename (r->fh), strerror (errno));
139 fh_close (fh,"data file", "rs");
149 /* Reads a record from the inline file into R.
150 Returns true if successful, false on failure. */
152 read_inline_record (struct dfm_reader *r)
154 if ((r->flags & DFM_SAW_BEGIN_DATA) == 0)
158 r->flags |= DFM_SAW_BEGIN_DATA;
160 /* FIXME: WTF can't this just be done with tokens?
161 Is this really a special case? */
166 if (!getl_read_line ())
168 msg (SE, _("BEGIN DATA expected."));
172 /* Skip leading whitespace, separate out first
173 word, so that S points to a single word reduced
175 s = ds_c_str (&getl_buf);
176 while (isspace ((unsigned char) *s))
178 for (cp = s; isalpha ((unsigned char) *cp); cp++)
179 *cp = tolower ((unsigned char) (*cp));
180 ds_truncate (&getl_buf, cp - s);
184 if (!lex_id_match_len ("begin", 5, s, strcspn (s, " \t\r\v\n")))
186 msg (SE, _("BEGIN DATA expected."));
187 lex_preprocess_line ();
190 getl_prompt = GETL_PRPT_DATA;
193 if (!getl_read_line ())
195 msg (SE, _("Unexpected end-of-file while reading data in BEGIN "
196 "DATA. This probably indicates "
197 "a missing or misformatted END DATA command. "
198 "END DATA must appear by itself on a single line "
199 "with exactly one space between words."));
203 if (ds_length (&getl_buf) >= 8
204 && !strncasecmp (ds_c_str (&getl_buf), "end data", 8))
206 lex_set_prog (ds_c_str (&getl_buf) + ds_length (&getl_buf));
210 ds_replace (&r->line, ds_c_str (&getl_buf));
214 /* Reads a record from a disk file into R.
215 Returns true if successful, false on failure. */
217 read_file_record (struct dfm_reader *r)
219 assert (r->fh != fh_inline_file ());
220 if (fh_get_mode (r->fh) == FH_MODE_TEXT)
223 if (!ds_gets (&r->line, r->file.file))
225 if (ferror (r->file.file))
227 msg (ME, _("Error reading file %s: %s."),
228 fh_get_name (r->fh), strerror (errno));
234 else if (fh_get_mode (r->fh) == FH_MODE_BINARY)
236 size_t record_width = fh_get_record_width (r->fh);
239 if (ds_length (&r->line) < record_width)
240 ds_rpad (&r->line, record_width, 0);
242 amt = fread (ds_c_str (&r->line), 1, record_width,
244 if (record_width != amt)
246 if (ferror (r->file.file))
247 msg (ME, _("Error reading file %s: %s."),
248 fh_get_name (r->fh), strerror (errno));
250 msg (ME, _("%s: Partial record at end of file."),
251 fh_get_name (r->fh));
262 r->where.line_number++;
267 /* Reads a record from R, setting the current position to the
268 start of the line. If an error occurs or end-of-file is
269 encountered, the current line is set to null. */
271 read_record (struct dfm_reader *r)
275 if (fh_get_referent (r->fh) == FH_REF_FILE)
276 success = read_file_record (r);
278 success = read_inline_record (r);
286 /* Returns nonzero if end of file has been reached on HANDLE.
287 Reads forward in HANDLE's file, if necessary to tell. */
289 dfm_eof (struct dfm_reader *r)
291 if (r->flags & DFM_ADVANCE)
293 r->flags &= ~DFM_ADVANCE;
294 if ((r->flags & DFM_EOF) == 0)
298 if (r->fh != fh_inline_file ())
299 msg (SE, _("Attempt to read beyond end-of-file on file %s."),
300 fh_get_name (r->fh));
302 msg (SE, _("Attempt to read beyond END DATA."));
307 return (r->flags & DFM_EOF) != 0;
310 /* Returns the current record in the file corresponding to
311 HANDLE. Aborts if reading from the file is necessary or at
312 end of file, so call dfm_eof() first. Sets *LINE to the line,
313 which is not null-terminated. The caller must not free or
314 modify the returned string. */
316 dfm_get_record (struct dfm_reader *r, struct fixed_string *line)
318 assert ((r->flags & DFM_ADVANCE) == 0);
319 assert ((r->flags & DFM_EOF) == 0);
320 assert (r->pos <= ds_length (&r->line));
322 line->string = ds_data (&r->line) + r->pos;
323 line->length = ds_length (&r->line) - r->pos;
326 /* Expands tabs in the current line into the equivalent number of
327 spaces, if appropriate for this kind of file. Aborts if
328 reading from the file is necessary or at end of file, so call
331 dfm_expand_tabs (struct dfm_reader *r)
334 size_t ofs, new_pos, tab_width;
336 assert ((r->flags & DFM_ADVANCE) == 0);
337 assert ((r->flags & DFM_EOF) == 0);
338 assert (r->pos <= ds_length (&r->line));
340 if (r->flags & DFM_TABS_EXPANDED)
342 r->flags |= DFM_TABS_EXPANDED;
344 if (r->fh != fh_inline_file ()
345 && (fh_get_mode (r->fh) == FH_MODE_BINARY
346 || fh_get_tab_width (r->fh) == 0
347 || memchr (ds_c_str (&r->line), '\t', ds_length (&r->line)) == NULL))
350 /* Expand tabs from r->line into r->scratch, and figure out
351 new value for r->pos. */
352 tab_width = fh_get_tab_width (r->fh);
353 ds_clear (&r->scratch);
355 for (ofs = 0; ofs < ds_length (&r->line); ofs++)
360 new_pos = ds_length (&r->scratch);
362 c = ds_c_str (&r->line)[ofs];
364 ds_putc (&r->scratch, c);
368 ds_putc (&r->scratch, ' ');
369 while (ds_length (&r->scratch) % tab_width != 0);
373 /* Swap r->line and r->scratch and set new r->pos. */
375 r->line = r->scratch;
380 /* Causes dfm_get_record() to read in the next record the next time it
381 is executed on file HANDLE. */
383 dfm_forward_record (struct dfm_reader *r)
385 r->flags |= DFM_ADVANCE;
388 /* Cancels the effect of any previous dfm_fwd_record() executed
389 on file HANDLE. Sets the current line to begin in the 1-based
392 dfm_reread_record (struct dfm_reader *r, size_t column)
394 r->flags &= ~DFM_ADVANCE;
397 else if (column > ds_length (&r->line))
398 r->pos = ds_length (&r->line);
403 /* Sets the current line to begin COLUMNS characters following
404 the current start. */
406 dfm_forward_columns (struct dfm_reader *r, size_t columns)
408 dfm_reread_record (r, (r->pos + 1) + columns);
411 /* Returns the 1-based column to which the line pointer in HANDLE
412 is set. Unless dfm_reread_record() or dfm_forward_columns()
413 have been called, this is 1. */
415 dfm_column_start (struct dfm_reader *r)
420 /* Pushes the filename and line number on the fn/ln stack. */
422 dfm_push (struct dfm_reader *r)
424 if (r->fh != fh_inline_file ())
425 err_push_file_locator (&r->where);
428 /* Pops the filename and line number from the fn/ln stack. */
430 dfm_pop (struct dfm_reader *r)
432 if (r->fh != fh_inline_file ())
433 err_pop_file_locator (&r->where);
436 /* BEGIN DATA...END DATA procedure. */
438 /* Perform BEGIN DATA...END DATA as a procedure in itself. */
440 cmd_begin_data (void)
442 struct dfm_reader *r;
444 if (!fh_is_open (fh_inline_file ()))
446 msg (SE, _("This command is not valid here since the current "
447 "input program does not access the inline file."));
452 /* Open inline file. */
453 r = dfm_open_reader (fh_inline_file ());
454 r->flags |= DFM_SAW_BEGIN_DATA;
456 /* Input procedure reads from inline file. */
457 getl_prompt = GETL_PRPT_DATA;
458 procedure (NULL, NULL);
460 dfm_close_reader (r);