1 /* PSPP - computes sample statistics.
2 Copyright (C) 1997-9, 2000 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., 59 Temple Place - Suite 330, Boston, MA
29 #include "file-handle.h"
37 #include "debug-print.h"
39 /* Flags for DFM readers. */
42 DFM_EOF = 001, /* At end-of-file? */
43 DFM_ADVANCE = 002, /* Read next line on dfm_get_record() call? */
44 DFM_SAW_BEGIN_DATA = 004, /* For inline_file only, whether we've
45 already read a BEGIN DATA line. */
46 DFM_TABS_EXPANDED = 010, /* Tabs have been expanded. */
49 /* file_handle extension structure. */
52 struct file_ext file; /* Associated file. */
54 struct file_locator where; /* Current location in data file. */
55 struct string line; /* Current line. */
56 size_t pos; /* Offset in line of current character. */
57 struct string scratch; /* Extra line buffer. */
58 enum dfm_reader_flags flags; /* Zero or more of DFM_*. */
61 static struct fh_ext_class dfm_r_class;
63 static void read_record (struct file_handle *h);
65 /* Asserts that H represents a DFM reader and returns H->ext
66 converted to a struct dfm_reader_ext *. */
67 static inline struct dfm_reader_ext *
68 get_reader (struct file_handle *h)
71 assert (h->class == &dfm_r_class);
72 assert (h->ext != NULL);
77 /* Closes file handle H opened by dfm_open_for_reading(). */
79 close_reader (struct file_handle *h)
81 struct dfm_reader_ext *ext = get_reader (h);
83 /* Skip any remaining data on the inline file. */
85 while ((ext->flags & DFM_EOF) == 0)
88 msg (VM (2), _("%s: Closing data-file handle %s."),
89 handle_get_filename (h), handle_get_name (h));
90 assert (h->class == &dfm_r_class);
93 fn_close_ext (&ext->file);
94 free (ext->file.filename);
95 ext->file.filename = NULL;
97 ds_destroy (&ext->line);
98 ds_destroy (&ext->scratch);
102 /* Opens a file handle for reading as a data file. Returns
103 nonzero only if successful. */
105 dfm_open_for_reading (struct file_handle *h)
107 struct dfm_reader_ext *ext;
109 if (h->class != NULL)
111 if (h->class == &dfm_r_class)
115 msg (ME, _("Cannot read from file %s already opened for %s."),
116 handle_get_name (h), gettext (h->class->name));
121 ext = xmalloc (sizeof *ext);
122 ext->where.filename = handle_get_filename (h);
123 ext->where.line_number = 0;
124 ext->file.file = NULL;
125 ds_init (&ext->line, 64);
126 ds_init (&ext->scratch, 0);
127 ext->flags = DFM_ADVANCE;
129 msg (VM (1), _("%s: Opening data-file handle %s for reading."),
130 handle_get_filename (h), handle_get_name (h));
133 if (h != inline_file)
135 ext->file.filename = xstrdup (handle_get_filename (h));
136 ext->file.mode = "rb";
137 ext->file.file = NULL;
138 ext->file.sequence_no = NULL;
139 ext->file.param = NULL;
140 ext->file.postopen = NULL;
141 ext->file.preclose = NULL;
142 if (!fn_open_ext (&ext->file))
144 msg (ME, _("Could not open \"%s\" for reading "
145 "as a data file: %s."),
146 handle_get_filename (h), strerror (errno));
151 h->class = &dfm_r_class;
161 /* Reads a record from H->EXT->FILE into H->EXT->LINE, setting
162 H->EXT->PTR to H->EXT->LINE, and setting H->EXT-LEN to the length
163 of the line. The line is not null-terminated. If an error occurs
164 or end-of-file is encountered, H->EXT->LINE is set to NULL. */
166 read_record (struct file_handle *h)
168 struct dfm_reader_ext *ext = get_reader (h);
170 if (h == inline_file)
172 if ((ext->flags & DFM_SAW_BEGIN_DATA) == 0)
176 ext->flags |= DFM_SAW_BEGIN_DATA;
178 /* FIXME: WTF can't this just be done with tokens?
179 Is this really a special case? */
184 if (!getl_read_line ())
186 msg (SE, _("BEGIN DATA expected."));
190 /* Skip leading whitespace, separate out first
191 word, so that S points to a single word reduced
193 s = ds_c_str (&getl_buf);
194 while (isspace ((unsigned char) *s))
196 for (cp = s; isalpha ((unsigned char) *cp); cp++)
197 *cp = tolower ((unsigned char) (*cp));
198 ds_truncate (&getl_buf, cp - s);
202 if (!lex_id_match_len ("begin", 5, s, strcspn (s, " \t\r\v\n")))
204 msg (SE, _("BEGIN DATA expected."));
205 lex_preprocess_line ();
208 getl_prompt = GETL_PRPT_DATA;
211 if (!getl_read_line ())
213 msg (SE, _("Unexpected end-of-file while reading data in BEGIN "
214 "DATA. This probably indicates "
215 "a missing or misformatted END DATA command. "
216 "END DATA must appear by itself on a single line "
217 "with exactly one space between words."));
221 ext->where.line_number++;
223 if (ds_length (&getl_buf) >= 8
224 && !strncasecmp (ds_c_str (&getl_buf), "end data", 8))
226 lex_set_prog (ds_c_str (&getl_buf) + ds_length (&getl_buf));
230 ds_replace (&ext->line, ds_c_str (&getl_buf));
234 if (handle_get_mode (h) == MODE_TEXT)
236 ds_clear (&ext->line);
237 if (!ds_gets (&ext->line, ext->file.file))
239 if (ferror (ext->file.file))
241 msg (ME, _("Error reading file %s: %s."),
242 handle_get_name (h), strerror (errno));
248 else if (handle_get_mode (h) == MODE_BINARY)
250 size_t record_width = handle_get_record_width (h);
253 if (ds_length (&ext->line) < record_width)
254 ds_rpad (&ext->line, record_width, 0);
256 amt = fread (ds_c_str (&ext->line), 1, record_width,
258 if (record_width != amt)
260 if (ferror (ext->file.file))
261 msg (ME, _("Error reading file %s: %s."),
262 handle_get_name (h), strerror (errno));
264 msg (ME, _("%s: Partial record at end of file."),
265 handle_get_name (h));
276 ext->where.line_number++;
283 /* Hit eof or an error, clean up everything. */
284 ext->flags |= DFM_EOF;
287 /* Returns nonzero if end of file has been reached on HANDLE.
288 Reads forward in HANDLE's file, if necessary to tell. */
290 dfm_eof (struct file_handle *h)
292 struct dfm_reader_ext *ext = get_reader (h);
293 if (ext->flags & DFM_ADVANCE)
295 ext->flags &= ~DFM_ADVANCE;
296 if ((ext->flags & DFM_EOF) == 0)
300 msg (SE, _("Attempt to read beyond end-of-file on file %s."),
301 handle_get_name (h));
306 return (ext->flags & DFM_EOF) != 0;
309 /* Returns the current record in the file corresponding to
310 HANDLE. Aborts if reading from the file is necessary or at
311 end of file, so call dfm_eof() first. Sets *LINE to the line,
312 which is not null-terminated. The caller must not free or
313 modify the returned string. */
315 dfm_get_record (struct file_handle *h, struct len_string *line)
317 struct dfm_reader_ext *ext = get_reader (h);
318 assert ((ext->flags & DFM_ADVANCE) == 0);
319 assert ((ext->flags & DFM_EOF) == 0);
320 assert (ext->pos <= ds_length (&ext->line));
322 line->string = ds_data (&ext->line) + ext->pos;
323 line->length = ds_length (&ext->line) - ext->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 file_handle *h)
333 struct dfm_reader_ext *ext = get_reader (h);
335 size_t ofs, new_pos, tab_width;
337 assert ((ext->flags & DFM_ADVANCE) == 0);
338 assert ((ext->flags & DFM_EOF) == 0);
339 assert (ext->pos <= ds_length (&ext->line));
341 if (ext->flags & DFM_TABS_EXPANDED)
343 ext->flags |= DFM_TABS_EXPANDED;
345 if (handle_get_mode (h) == MODE_BINARY
346 || handle_get_tab_width (h) == 0
347 || memchr (ds_c_str (&ext->line), '\t', ds_length (&ext->line)) == NULL)
350 /* Expand tabs from ext->line into ext->scratch, and figure out
351 new value for ext->pos. */
352 tab_width = handle_get_tab_width (h);
353 ds_clear (&ext->scratch);
355 for (ofs = 0; ofs < ds_length (&ext->line); ofs++)
360 new_pos = ds_length (&ext->scratch);
362 c = ds_c_str (&ext->line)[ofs];
364 ds_putc (&ext->scratch, c);
368 ds_putc (&ext->scratch, ' ');
369 while (ds_length (&ext->scratch) % tab_width != 0);
373 /* Swap ext->line and ext->scratch and set new ext->pos. */
375 ext->line = ext->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 file_handle *h)
385 struct dfm_reader_ext *ext = get_reader (h);
386 ext->flags |= DFM_ADVANCE;
389 /* Cancels the effect of any previous dfm_fwd_record() executed
390 on file HANDLE. Sets the current line to begin in the 1-based
393 dfm_reread_record (struct file_handle *h, size_t column)
395 struct dfm_reader_ext *ext = get_reader (h);
396 ext->flags &= ~DFM_ADVANCE;
399 else if (column > ds_length (&ext->line))
400 ext->pos = ds_length (&ext->line);
402 ext->pos = column - 1;
405 /* Sets the current line to begin COLUMNS characters following
406 the current start. */
408 dfm_forward_columns (struct file_handle *h, size_t columns)
410 struct dfm_reader_ext *ext = get_reader (h);
411 dfm_reread_record (h, (ext->pos + 1) + columns);
414 /* Returns the 1-based column to which the line pointer in HANDLE
415 is set. Unless dfm_reread_record() or dfm_forward_columns()
416 have been called, this is 1. */
418 dfm_column_start (struct file_handle *h)
420 struct dfm_reader_ext *ext = get_reader (h);
424 /* Pushes the filename and line number on the fn/ln stack. */
426 dfm_push (struct file_handle *h)
428 struct dfm_reader_ext *ext = get_reader (h);
429 if (h != inline_file)
430 err_push_file_locator (&ext->where);
433 /* Pops the filename and line number from the fn/ln stack. */
435 dfm_pop (struct file_handle *h)
437 struct dfm_reader_ext *ext = get_reader (h);
438 if (h != inline_file)
439 err_pop_file_locator (&ext->where);
442 /* DFM reader class. */
443 static struct fh_ext_class dfm_r_class =
446 N_("reading as a data file"),
450 /* file_handle extension structure. */
451 struct dfm_writer_ext
453 struct file_ext file; /* Associated file. */
454 struct file_locator where; /* Current location in data file. */
455 char *bounce; /* Bounce buffer for fixed-size fields. */
458 static struct fh_ext_class dfm_w_class;
460 /* Opens a file handle for writing as a data file. */
462 dfm_open_for_writing (struct file_handle *h)
464 struct dfm_writer_ext *ext;
466 if (h->class != NULL)
468 if (h->class == &dfm_w_class)
472 msg (ME, _("Cannot write to file %s already opened for %s."),
473 handle_get_name (h), gettext (h->class->name));
479 ext = xmalloc (sizeof *ext);
480 ext->where.filename = handle_get_filename (h);
481 ext->where.line_number = 0;
482 ext->file.file = NULL;
485 msg (VM (1), _("%s: Opening data-file handle %s for writing."),
486 handle_get_filename (h), handle_get_name (h));
489 if (h == inline_file)
491 msg (ME, _("Cannot open the inline file for writing."));
495 ext->file.filename = xstrdup (handle_get_filename (h));
496 ext->file.mode = "wb";
497 ext->file.file = NULL;
498 ext->file.sequence_no = NULL;
499 ext->file.param = NULL;
500 ext->file.postopen = NULL;
501 ext->file.preclose = NULL;
503 if (!fn_open_ext (&ext->file))
505 msg (ME, _("An error occurred while opening \"%s\" for writing "
506 "as a data file: %s."),
507 handle_get_filename (h), strerror (errno));
511 h->class = &dfm_w_class;
521 /* Writes record REC having length LEN to the file corresponding to
522 HANDLE. REC is not null-terminated. Returns nonzero on success,
525 dfm_put_record (struct file_handle *h, const char *rec, size_t len)
527 struct dfm_writer_ext *ext;
530 assert (h->class == &dfm_w_class);
531 assert (h->ext != NULL);
534 if (handle_get_mode (h) == MODE_BINARY && len < handle_get_record_width (h))
536 size_t rec_width = handle_get_record_width (h);
537 if (ext->bounce == NULL)
538 ext->bounce = xmalloc (rec_width);
539 memcpy (ext->bounce, rec, len);
540 memset (&ext->bounce[len], 0, rec_width - len);
545 if (fwrite (rec, len, 1, ext->file.file) != 1)
547 msg (ME, _("Error writing file %s: %s."),
548 handle_get_name (h), strerror (errno));
556 /* Closes file handle H opened by dfm_open_for_writing(). */
558 close_writer (struct file_handle *h)
560 struct dfm_writer_ext *ext;
562 assert (h->class == &dfm_w_class);
565 msg (VM (2), _("%s: Closing data-file handle %s."),
566 handle_get_filename (h), handle_get_name (h));
569 fn_close_ext (&ext->file);
570 free (ext->file.filename);
571 ext->file.filename = NULL;
577 /* DFM writer class. */
578 static struct fh_ext_class dfm_w_class =
581 N_("writing as a data file"),
585 /* BEGIN DATA...END DATA procedure. */
587 /* Perform BEGIN DATA...END DATA as a procedure in itself. */
589 cmd_begin_data (void)
591 struct dfm_reader_ext *ext;
593 /* FIXME: figure out the *exact* conditions, not these really
594 lenient conditions. */
595 if (vfm_source == NULL
596 || case_source_is_class (vfm_source, &storage_source_class))
598 msg (SE, _("This command is not valid here since the current "
599 "input program does not access the inline file."));
604 /* Initialize inline_file. */
605 msg (VM (1), _("inline file: Opening for reading."));
606 dfm_open_for_reading (inline_file);
607 ext = inline_file->ext;
608 ext->flags |= DFM_SAW_BEGIN_DATA;
610 /* We don't actually read from the inline file. The input procedure
611 is what reads from it. */
612 getl_prompt = GETL_PRPT_DATA;
613 procedure (NULL, NULL);
615 ext = inline_file->ext;
616 if (ext && (ext->flags & DFM_EOF) == 0)
618 msg (MW, _("Skipping remaining inline data."));
619 while ((ext->flags & DFM_EOF) == 0)
620 read_record (inline_file);
622 assert (inline_file->ext == NULL);