1 /* PSPP - computes sample statistics.
2 Copyright (C) 1997-2004 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"
36 #include "debug-print.h"
38 /* Flags for DFM readers. */
41 DFM_EOF = 001, /* At end-of-file? */
42 DFM_ADVANCE = 002, /* Read next line on dfm_get_record() call? */
43 DFM_SAW_BEGIN_DATA = 004, /* For inline_file only, whether we've
44 already read a BEGIN DATA line. */
45 DFM_TABS_EXPANDED = 010, /* Tabs have been expanded. */
48 /* Data file reader. */
51 struct file_handle *fh; /* File handle. */
52 struct file_ext file; /* Associated file. */
53 struct file_locator where; /* Current location in data file. */
54 struct string line; /* Current line. */
55 size_t pos; /* Offset in line of current character. */
56 struct string scratch; /* Extra line buffer. */
57 enum dfm_reader_flags flags; /* Zero or more of DFM_*. */
60 static int inline_open_cnt;
61 static struct dfm_reader *inline_file;
63 static void read_record (struct dfm_reader *r);
65 /* Closes reader R opened by dfm_open_reader(). */
67 dfm_close_reader (struct dfm_reader *r)
75 still_open = fh_close (r->fh, "data file", "rs");
78 assert (inline_open_cnt > 0);
79 still_open = --inline_open_cnt;
83 /* Skip any remaining data on the inline file. */
84 if (r->flags & DFM_SAW_BEGIN_DATA)
85 while ((r->flags & DFM_EOF) == 0)
93 if (r->fh != NULL && r->file.file)
95 fn_close_ext (&r->file);
96 free (r->file.filename);
97 r->file.filename = NULL;
99 ds_destroy (&r->line);
100 ds_destroy (&r->scratch);
104 /* Opens the file designated by file handle FH for reading as a
105 data file. Providing a null pointer for FH designates the
106 "inline file", that is, data included inline in the command
107 file between BEGIN FILE and END FILE. Returns nonzero only if
110 dfm_open_reader (struct file_handle *fh)
112 struct dfm_reader *r;
117 rp = fh_open (fh, "data file", "rs");
125 assert (inline_open_cnt >= 0);
126 if (inline_open_cnt++ > 0)
131 r = xmalloc (sizeof *r);
135 r->where.filename = handle_get_filename (fh);
136 r->where.line_number = 0;
139 ds_init (&r->line, 64);
140 ds_init (&r->scratch, 0);
141 r->flags = DFM_ADVANCE;
145 r->file.filename = xstrdup (handle_get_filename (r->fh));
148 r->file.sequence_no = NULL;
149 r->file.param = NULL;
150 r->file.postopen = NULL;
151 r->file.preclose = NULL;
152 if (!fn_open_ext (&r->file))
154 msg (ME, _("Could not open \"%s\" for reading "
155 "as a data file: %s."),
156 handle_get_filename (r->fh), strerror (errno));
158 fh_close (fh,"data file", "rs");
171 read_inline_record (struct dfm_reader *r)
173 if ((r->flags & DFM_SAW_BEGIN_DATA) == 0)
177 r->flags |= DFM_SAW_BEGIN_DATA;
179 /* FIXME: WTF can't this just be done with tokens?
180 Is this really a special case? */
185 if (!getl_read_line ())
187 msg (SE, _("BEGIN DATA expected."));
191 /* Skip leading whitespace, separate out first
192 word, so that S points to a single word reduced
194 s = ds_c_str (&getl_buf);
195 while (isspace ((unsigned char) *s))
197 for (cp = s; isalpha ((unsigned char) *cp); cp++)
198 *cp = tolower ((unsigned char) (*cp));
199 ds_truncate (&getl_buf, cp - s);
203 if (!lex_id_match_len ("begin", 5, s, strcspn (s, " \t\r\v\n")))
205 msg (SE, _("BEGIN DATA expected."));
206 lex_preprocess_line ();
209 getl_prompt = GETL_PRPT_DATA;
212 if (!getl_read_line ())
214 msg (SE, _("Unexpected end-of-file while reading data in BEGIN "
215 "DATA. This probably indicates "
216 "a missing or misformatted END DATA command. "
217 "END DATA must appear by itself on a single line "
218 "with exactly one space between words."));
223 r->where.line_number++;
225 if (ds_length (&getl_buf) >= 8
226 && !strncasecmp (ds_c_str (&getl_buf), "end data", 8))
228 lex_set_prog (ds_c_str (&getl_buf) + ds_length (&getl_buf));
232 ds_replace (&r->line, ds_c_str (&getl_buf));
237 read_file_record (struct dfm_reader *r)
239 assert (r->fh != NULL);
240 if (handle_get_mode (r->fh) == MODE_TEXT)
243 if (!ds_gets (&r->line, r->file.file))
245 if (ferror (r->file.file))
247 msg (ME, _("Error reading file %s: %s."),
248 handle_get_name (r->fh), strerror (errno));
254 else if (handle_get_mode (r->fh) == MODE_BINARY)
256 size_t record_width = handle_get_record_width (r->fh);
259 if (ds_length (&r->line) < record_width)
260 ds_rpad (&r->line, record_width, 0);
262 amt = fread (ds_c_str (&r->line), 1, record_width,
264 if (record_width != amt)
266 if (ferror (r->file.file))
267 msg (ME, _("Error reading file %s: %s."),
268 handle_get_name (r->fh), strerror (errno));
270 msg (ME, _("%s: Partial record at end of file."),
271 handle_get_name (r->fh));
282 r->where.line_number++;
287 /* Reads a record from R, setting the current position to the
288 start of the line. If an error occurs or end-of-file is
289 encountered, the current line is set to null. */
291 read_record (struct dfm_reader *r)
293 int success = r->fh != NULL ? read_file_record (r) : read_inline_record (r);
300 /* Returns nonzero if end of file has been reached on HANDLE.
301 Reads forward in HANDLE's file, if necessary to tell. */
303 dfm_eof (struct dfm_reader *r)
305 if (r->flags & DFM_ADVANCE)
307 r->flags &= ~DFM_ADVANCE;
308 if ((r->flags & DFM_EOF) == 0)
313 msg (SE, _("Attempt to read beyond end-of-file on file %s."),
314 handle_get_name (r->fh));
316 msg (SE, _("Attempt to read beyond END DATA."));
321 return (r->flags & DFM_EOF) != 0;
324 /* Returns the current record in the file corresponding to
325 HANDLE. Aborts if reading from the file is necessary or at
326 end of file, so call dfm_eof() first. Sets *LINE to the line,
327 which is not null-terminated. The caller must not free or
328 modify the returned string. */
330 dfm_get_record (struct dfm_reader *r, struct fixed_string *line)
332 assert ((r->flags & DFM_ADVANCE) == 0);
333 assert ((r->flags & DFM_EOF) == 0);
334 assert (r->pos <= ds_length (&r->line));
336 line->string = ds_data (&r->line) + r->pos;
337 line->length = ds_length (&r->line) - r->pos;
340 /* Expands tabs in the current line into the equivalent number of
341 spaces, if appropriate for this kind of file. Aborts if
342 reading from the file is necessary or at end of file, so call
345 dfm_expand_tabs (struct dfm_reader *r)
348 size_t ofs, new_pos, tab_width;
350 assert ((r->flags & DFM_ADVANCE) == 0);
351 assert ((r->flags & DFM_EOF) == 0);
352 assert (r->pos <= ds_length (&r->line));
354 if (r->flags & DFM_TABS_EXPANDED)
356 r->flags |= DFM_TABS_EXPANDED;
359 && (handle_get_mode (r->fh) == MODE_BINARY
360 || handle_get_tab_width (r->fh) == 0
361 || memchr (ds_c_str (&r->line), '\t', ds_length (&r->line)) == NULL))
364 /* Expand tabs from r->line into r->scratch, and figure out
365 new value for r->pos. */
366 tab_width = r->fh != NULL ? handle_get_tab_width (r->fh) : 8;
367 ds_clear (&r->scratch);
369 for (ofs = 0; ofs < ds_length (&r->line); ofs++)
374 new_pos = ds_length (&r->scratch);
376 c = ds_c_str (&r->line)[ofs];
378 ds_putc (&r->scratch, c);
382 ds_putc (&r->scratch, ' ');
383 while (ds_length (&r->scratch) % tab_width != 0);
387 /* Swap r->line and r->scratch and set new r->pos. */
389 r->line = r->scratch;
394 /* Causes dfm_get_record() to read in the next record the next time it
395 is executed on file HANDLE. */
397 dfm_forward_record (struct dfm_reader *r)
399 r->flags |= DFM_ADVANCE;
402 /* Cancels the effect of any previous dfm_fwd_record() executed
403 on file HANDLE. Sets the current line to begin in the 1-based
406 dfm_reread_record (struct dfm_reader *r, size_t column)
408 r->flags &= ~DFM_ADVANCE;
411 else if (column > ds_length (&r->line))
412 r->pos = ds_length (&r->line);
417 /* Sets the current line to begin COLUMNS characters following
418 the current start. */
420 dfm_forward_columns (struct dfm_reader *r, size_t columns)
422 dfm_reread_record (r, (r->pos + 1) + columns);
425 /* Returns the 1-based column to which the line pointer in HANDLE
426 is set. Unless dfm_reread_record() or dfm_forward_columns()
427 have been called, this is 1. */
429 dfm_column_start (struct dfm_reader *r)
434 /* Pushes the filename and line number on the fn/ln stack. */
436 dfm_push (struct dfm_reader *r)
439 err_push_file_locator (&r->where);
442 /* Pops the filename and line number from the fn/ln stack. */
444 dfm_pop (struct dfm_reader *r)
447 err_pop_file_locator (&r->where);
450 /* BEGIN DATA...END DATA procedure. */
452 /* Perform BEGIN DATA...END DATA as a procedure in itself. */
454 cmd_begin_data (void)
456 struct dfm_reader *r;
458 /* FIXME: figure out the *exact* conditions, not these really
459 lenient conditions. */
460 if (vfm_source == NULL
461 || case_source_is_class (vfm_source, &storage_source_class))
463 msg (SE, _("This command is not valid here since the current "
464 "input program does not access the inline file."));
469 /* Open inline file. */
470 r = dfm_open_reader (NULL);
471 r->flags |= DFM_SAW_BEGIN_DATA;
473 /* Input procedure reads from inline file. */
474 getl_prompt = GETL_PRPT_DATA;
475 procedure (NULL, NULL);
477 dfm_close_reader (r);